1use std::collections::{HashMap, VecDeque};
14use std::time::{Duration, Instant};
15
16#[derive(Debug, Clone)]
20pub struct VectorCacheConfig {
21 pub max_entries: usize,
23 pub max_memory_bytes: usize,
25 pub default_ttl: Option<Duration>,
27}
28
29impl Default for VectorCacheConfig {
30 fn default() -> Self {
31 Self {
32 max_entries: 10_000,
33 max_memory_bytes: 0,
34 default_ttl: None,
35 }
36 }
37}
38
39#[derive(Debug, Clone, Default)]
41pub struct CacheStatistics {
42 pub hits: u64,
44 pub misses: u64,
46 pub inserts: u64,
48 pub evictions: u64,
50 pub expirations: u64,
52 pub invalidations: u64,
54}
55
56impl CacheStatistics {
57 pub fn hit_ratio(&self) -> f64 {
59 let total = self.hits + self.misses;
60 if total == 0 {
61 return 0.0;
62 }
63 self.hits as f64 / total as f64
64 }
65
66 pub fn total_requests(&self) -> u64 {
68 self.hits + self.misses
69 }
70}
71
72#[derive(Debug, Clone)]
74struct CacheEntry {
75 vector: Vec<f64>,
77 inserted_at: Instant,
79 last_accessed: Instant,
81 ttl: Option<Duration>,
83}
84
85impl CacheEntry {
86 fn memory_bytes(&self) -> usize {
87 std::mem::size_of::<Self>() + self.vector.len() * std::mem::size_of::<f64>()
89 }
90
91 fn is_expired(&self, default_ttl: Option<Duration>) -> bool {
92 let ttl = self.ttl.or(default_ttl);
93 if let Some(duration) = ttl {
94 self.inserted_at.elapsed() > duration
95 } else {
96 false
97 }
98 }
99}
100
101#[derive(Debug, Clone)]
103pub struct CacheSnapshot {
104 pub entries: Vec<(String, Vec<f64>)>,
106 pub entry_count: usize,
108}
109
110pub struct VectorCache {
114 store: HashMap<String, CacheEntry>,
116 lru_order: VecDeque<String>,
118 config: VectorCacheConfig,
120 stats: CacheStatistics,
122}
123
124impl VectorCache {
125 pub fn new() -> Self {
127 Self::with_config(VectorCacheConfig::default())
128 }
129
130 pub fn with_config(config: VectorCacheConfig) -> Self {
132 Self {
133 store: HashMap::new(),
134 lru_order: VecDeque::new(),
135 config,
136 stats: CacheStatistics::default(),
137 }
138 }
139
140 pub fn get(&mut self, key: &str) -> Option<Vec<f64>> {
146 if let Some(entry) = self.store.get(key) {
148 if entry.is_expired(self.config.default_ttl) {
149 let key_owned = key.to_string();
151 self.store.remove(&key_owned);
152 self.lru_order.retain(|k| k != &key_owned);
153 self.stats.expirations += 1;
154 self.stats.misses += 1;
155 return None;
156 }
157 } else {
158 self.stats.misses += 1;
159 return None;
160 }
161
162 self.touch(key);
164 self.stats.hits += 1;
165
166 if let Some(entry) = self.store.get_mut(key) {
168 entry.last_accessed = Instant::now();
169 Some(entry.vector.clone())
170 } else {
171 None
172 }
173 }
174
175 pub fn put(&mut self, key: impl Into<String>, vector: Vec<f64>) {
177 self.put_with_ttl(key, vector, None);
178 }
179
180 pub fn put_with_ttl(
182 &mut self,
183 key: impl Into<String>,
184 vector: Vec<f64>,
185 ttl: Option<Duration>,
186 ) {
187 let key = key.into();
188 let now = Instant::now();
189
190 let entry = CacheEntry {
191 vector,
192 inserted_at: now,
193 last_accessed: now,
194 ttl,
195 };
196
197 if self.store.contains_key(&key) {
199 self.lru_order.retain(|k| k != &key);
200 }
201
202 self.store.insert(key.clone(), entry);
203 self.lru_order.push_back(key);
204 self.stats.inserts += 1;
205
206 self.enforce_entry_limit();
208 self.enforce_memory_limit();
209 }
210
211 pub fn batch_get(&mut self, keys: &[&str]) -> HashMap<String, Vec<f64>> {
215 let mut result = HashMap::new();
216 for &key in keys {
217 if let Some(vec) = self.get(key) {
218 result.insert(key.to_string(), vec);
219 }
220 }
221 result
222 }
223
224 pub fn batch_put(&mut self, entries: Vec<(String, Vec<f64>)>) {
226 for (key, vector) in entries {
227 self.put(key, vector);
228 }
229 }
230
231 pub fn warm(&mut self, entries: Vec<(String, Vec<f64>)>) -> usize {
237 let mut loaded = 0;
238 for (key, vector) in entries {
239 if !self.store.contains_key(&key) {
240 self.put(key, vector);
241 loaded += 1;
242 }
243 }
244 loaded
245 }
246
247 pub fn invalidate(&mut self, key: &str) -> bool {
251 if self.store.remove(key).is_some() {
252 self.lru_order.retain(|k| k != key);
253 self.stats.invalidations += 1;
254 true
255 } else {
256 false
257 }
258 }
259
260 pub fn invalidate_prefix(&mut self, prefix: &str) -> usize {
262 let keys_to_remove: Vec<String> = self
263 .store
264 .keys()
265 .filter(|k| k.starts_with(prefix))
266 .cloned()
267 .collect();
268
269 let count = keys_to_remove.len();
270 for key in &keys_to_remove {
271 self.store.remove(key);
272 }
273 self.lru_order.retain(|k| !k.starts_with(prefix));
274 self.stats.invalidations += count as u64;
275 count
276 }
277
278 pub fn clear(&mut self) {
280 let count = self.store.len() as u64;
281 self.store.clear();
282 self.lru_order.clear();
283 self.stats.invalidations += count;
284 }
285
286 pub fn statistics(&self) -> &CacheStatistics {
290 &self.stats
291 }
292
293 pub fn reset_statistics(&mut self) {
295 self.stats = CacheStatistics::default();
296 }
297
298 pub fn len(&self) -> usize {
300 self.store.len()
301 }
302
303 pub fn is_empty(&self) -> bool {
305 self.store.is_empty()
306 }
307
308 pub fn memory_usage(&self) -> usize {
310 self.store.values().map(|e| e.memory_bytes()).sum::<usize>()
311 + self.lru_order.len() * std::mem::size_of::<String>()
312 }
313
314 pub fn contains_key(&self, key: &str) -> bool {
316 self.store.contains_key(key)
317 }
318
319 pub fn snapshot(&self) -> CacheSnapshot {
323 let entries: Vec<(String, Vec<f64>)> = self
324 .store
325 .iter()
326 .filter(|(_, e)| !e.is_expired(self.config.default_ttl))
327 .map(|(k, e)| (k.clone(), e.vector.clone()))
328 .collect();
329 let entry_count = entries.len();
330 CacheSnapshot {
331 entries,
332 entry_count,
333 }
334 }
335
336 pub fn load_snapshot(&mut self, snapshot: CacheSnapshot) -> usize {
338 let count = snapshot.entries.len();
339 for (key, vector) in snapshot.entries {
340 self.put(key, vector);
341 }
342 count
343 }
344
345 pub fn sweep_expired(&mut self) -> usize {
351 let expired_keys: Vec<String> = self
352 .store
353 .iter()
354 .filter(|(_, e)| e.is_expired(self.config.default_ttl))
355 .map(|(k, _)| k.clone())
356 .collect();
357
358 let count = expired_keys.len();
359 for key in &expired_keys {
360 self.store.remove(key);
361 }
362 self.lru_order.retain(|k| !expired_keys.contains(k));
363 self.stats.expirations += count as u64;
364 count
365 }
366
367 fn touch(&mut self, key: &str) {
371 self.lru_order.retain(|k| k != key);
372 self.lru_order.push_back(key.to_string());
373 }
374
375 fn enforce_entry_limit(&mut self) {
377 if self.config.max_entries == 0 {
378 return;
379 }
380 while self.store.len() > self.config.max_entries {
381 if let Some(oldest) = self.lru_order.pop_front() {
382 self.store.remove(&oldest);
383 self.stats.evictions += 1;
384 } else {
385 break;
386 }
387 }
388 }
389
390 fn enforce_memory_limit(&mut self) {
392 if self.config.max_memory_bytes == 0 {
393 return;
394 }
395 while self.memory_usage() > self.config.max_memory_bytes {
396 if let Some(oldest) = self.lru_order.pop_front() {
397 self.store.remove(&oldest);
398 self.stats.evictions += 1;
399 } else {
400 break;
401 }
402 }
403 }
404}
405
406impl Default for VectorCache {
407 fn default() -> Self {
408 Self::new()
409 }
410}
411
412#[cfg(test)]
417mod tests {
418 use super::*;
419
420 fn vec3(a: f64, b: f64, c: f64) -> Vec<f64> {
421 vec![a, b, c]
422 }
423
424 #[test]
427 fn test_put_and_get() {
428 let mut cache = VectorCache::new();
429 cache.put("k1", vec3(1.0, 2.0, 3.0));
430 let v = cache.get("k1");
431 assert!(v.is_some());
432 assert_eq!(v.expect("should exist"), vec3(1.0, 2.0, 3.0));
433 }
434
435 #[test]
436 fn test_get_miss() {
437 let mut cache = VectorCache::new();
438 assert!(cache.get("nonexistent").is_none());
439 assert_eq!(cache.statistics().misses, 1);
440 }
441
442 #[test]
443 fn test_put_overwrite() {
444 let mut cache = VectorCache::new();
445 cache.put("k1", vec3(1.0, 2.0, 3.0));
446 cache.put("k1", vec3(4.0, 5.0, 6.0));
447 let v = cache.get("k1");
448 assert_eq!(v.expect("should exist"), vec3(4.0, 5.0, 6.0));
449 assert_eq!(cache.len(), 1);
450 }
451
452 #[test]
455 fn test_lru_eviction() {
456 let config = VectorCacheConfig {
457 max_entries: 2,
458 ..Default::default()
459 };
460 let mut cache = VectorCache::with_config(config);
461 cache.put("a", vec3(1.0, 0.0, 0.0));
462 cache.put("b", vec3(0.0, 1.0, 0.0));
463 cache.put("c", vec3(0.0, 0.0, 1.0)); assert!(cache.get("a").is_none());
466 assert!(cache.get("b").is_some());
467 assert!(cache.get("c").is_some());
468 assert_eq!(cache.statistics().evictions, 1);
469 }
470
471 #[test]
472 fn test_lru_access_refreshes() {
473 let config = VectorCacheConfig {
474 max_entries: 2,
475 ..Default::default()
476 };
477 let mut cache = VectorCache::with_config(config);
478 cache.put("a", vec3(1.0, 0.0, 0.0));
479 cache.put("b", vec3(0.0, 1.0, 0.0));
480
481 let _ = cache.get("a");
483
484 cache.put("c", vec3(0.0, 0.0, 1.0)); assert!(cache.get("a").is_some());
487 assert!(cache.get("b").is_none());
488 assert!(cache.get("c").is_some());
489 }
490
491 #[test]
494 fn test_memory_limit_eviction() {
495 let entry_size = std::mem::size_of::<CacheEntry>() + 3 * std::mem::size_of::<f64>();
496 let config = VectorCacheConfig {
497 max_entries: 0, max_memory_bytes: entry_size * 2 + 100, default_ttl: None,
500 };
501 let mut cache = VectorCache::with_config(config);
502 cache.put("a", vec3(1.0, 0.0, 0.0));
503 cache.put("b", vec3(0.0, 1.0, 0.0));
504 cache.put("c", vec3(0.0, 0.0, 1.0));
505
506 assert!(cache.len() <= 3);
508 let _ = cache.statistics().evictions;
509 }
510
511 #[test]
514 fn test_ttl_expiry() {
515 let config = VectorCacheConfig {
516 default_ttl: Some(Duration::from_millis(1)),
517 ..Default::default()
518 };
519 let mut cache = VectorCache::with_config(config);
520 cache.put("k1", vec3(1.0, 2.0, 3.0));
521
522 std::thread::sleep(Duration::from_millis(10));
524
525 assert!(cache.get("k1").is_none());
526 assert!(cache.statistics().expirations >= 1);
527 }
528
529 #[test]
530 fn test_per_entry_ttl() {
531 let mut cache = VectorCache::new();
532 cache.put_with_ttl("k1", vec3(1.0, 2.0, 3.0), Some(Duration::from_millis(1)));
533 cache.put("k2", vec3(4.0, 5.0, 6.0)); std::thread::sleep(Duration::from_millis(10));
536
537 assert!(cache.get("k1").is_none()); assert!(cache.get("k2").is_some()); }
540
541 #[test]
542 fn test_sweep_expired() {
543 let config = VectorCacheConfig {
544 default_ttl: Some(Duration::from_millis(1)),
545 ..Default::default()
546 };
547 let mut cache = VectorCache::with_config(config);
548 cache.put("a", vec3(1.0, 0.0, 0.0));
549 cache.put("b", vec3(0.0, 1.0, 0.0));
550
551 std::thread::sleep(Duration::from_millis(10));
552
553 let swept = cache.sweep_expired();
554 assert_eq!(swept, 2);
555 assert!(cache.is_empty());
556 }
557
558 #[test]
561 fn test_invalidate_key() {
562 let mut cache = VectorCache::new();
563 cache.put("k1", vec3(1.0, 2.0, 3.0));
564 assert!(cache.invalidate("k1"));
565 assert!(cache.get("k1").is_none());
566 assert_eq!(cache.statistics().invalidations, 1);
567 }
568
569 #[test]
570 fn test_invalidate_nonexistent() {
571 let mut cache = VectorCache::new();
572 assert!(!cache.invalidate("nope"));
573 }
574
575 #[test]
576 fn test_invalidate_prefix() {
577 let mut cache = VectorCache::new();
578 cache.put("user:1", vec3(1.0, 0.0, 0.0));
579 cache.put("user:2", vec3(0.0, 1.0, 0.0));
580 cache.put("item:1", vec3(0.0, 0.0, 1.0));
581
582 let removed = cache.invalidate_prefix("user:");
583 assert_eq!(removed, 2);
584 assert_eq!(cache.len(), 1);
585 assert!(cache.contains_key("item:1"));
586 }
587
588 #[test]
589 fn test_clear() {
590 let mut cache = VectorCache::new();
591 cache.put("a", vec3(1.0, 0.0, 0.0));
592 cache.put("b", vec3(0.0, 1.0, 0.0));
593 cache.clear();
594 assert!(cache.is_empty());
595 }
596
597 #[test]
600 fn test_batch_put_and_get() {
601 let mut cache = VectorCache::new();
602 cache.batch_put(vec![
603 ("k1".to_string(), vec3(1.0, 0.0, 0.0)),
604 ("k2".to_string(), vec3(0.0, 1.0, 0.0)),
605 ("k3".to_string(), vec3(0.0, 0.0, 1.0)),
606 ]);
607 assert_eq!(cache.len(), 3);
608
609 let results = cache.batch_get(&["k1", "k3", "missing"]);
610 assert_eq!(results.len(), 2);
611 assert!(results.contains_key("k1"));
612 assert!(results.contains_key("k3"));
613 }
614
615 #[test]
618 fn test_warm() {
619 let mut cache = VectorCache::new();
620 cache.put("existing", vec3(9.0, 9.0, 9.0));
621
622 let loaded = cache.warm(vec![
623 ("existing".to_string(), vec3(0.0, 0.0, 0.0)), ("new1".to_string(), vec3(1.0, 0.0, 0.0)),
625 ("new2".to_string(), vec3(0.0, 1.0, 0.0)),
626 ]);
627
628 assert_eq!(loaded, 2);
629 assert_eq!(cache.len(), 3);
630
631 let v = cache.get("existing").expect("should exist");
633 assert_eq!(v, vec3(9.0, 9.0, 9.0));
634 }
635
636 #[test]
639 fn test_hit_ratio() {
640 let mut cache = VectorCache::new();
641 cache.put("k1", vec3(1.0, 2.0, 3.0));
642 let _ = cache.get("k1"); let _ = cache.get("k2"); let stats = cache.statistics();
646 assert_eq!(stats.hits, 1);
647 assert_eq!(stats.misses, 1);
648 assert!((stats.hit_ratio() - 0.5).abs() < f64::EPSILON);
649 assert_eq!(stats.total_requests(), 2);
650 }
651
652 #[test]
653 fn test_hit_ratio_no_requests() {
654 let cache = VectorCache::new();
655 assert!((cache.statistics().hit_ratio() - 0.0).abs() < f64::EPSILON);
656 }
657
658 #[test]
659 fn test_reset_statistics() {
660 let mut cache = VectorCache::new();
661 cache.put("k1", vec3(1.0, 2.0, 3.0));
662 let _ = cache.get("k1");
663 cache.reset_statistics();
664 assert_eq!(cache.statistics().hits, 0);
665 assert_eq!(cache.statistics().inserts, 0);
666 }
667
668 #[test]
671 fn test_snapshot_and_load() {
672 let mut cache = VectorCache::new();
673 cache.put("a", vec3(1.0, 0.0, 0.0));
674 cache.put("b", vec3(0.0, 1.0, 0.0));
675
676 let snap = cache.snapshot();
677 assert_eq!(snap.entry_count, 2);
678
679 let mut cache2 = VectorCache::new();
680 let loaded = cache2.load_snapshot(snap);
681 assert_eq!(loaded, 2);
682 assert!(cache2.get("a").is_some());
683 assert!(cache2.get("b").is_some());
684 }
685
686 #[test]
687 fn test_snapshot_excludes_expired() {
688 let config = VectorCacheConfig {
689 default_ttl: Some(Duration::from_millis(1)),
690 ..Default::default()
691 };
692 let mut cache = VectorCache::with_config(config);
693 cache.put("x", vec3(1.0, 2.0, 3.0));
694 std::thread::sleep(Duration::from_millis(10));
695
696 let snap = cache.snapshot();
697 assert_eq!(snap.entry_count, 0);
698 }
699
700 #[test]
703 fn test_len_and_empty() {
704 let mut cache = VectorCache::new();
705 assert!(cache.is_empty());
706 assert_eq!(cache.len(), 0);
707
708 cache.put("k1", vec3(1.0, 2.0, 3.0));
709 assert!(!cache.is_empty());
710 assert_eq!(cache.len(), 1);
711 }
712
713 #[test]
714 fn test_contains_key() {
715 let mut cache = VectorCache::new();
716 cache.put("k1", vec3(1.0, 2.0, 3.0));
717 assert!(cache.contains_key("k1"));
718 assert!(!cache.contains_key("k2"));
719 }
720
721 #[test]
724 fn test_memory_usage_grows() {
725 let mut cache = VectorCache::new();
726 let m0 = cache.memory_usage();
727 cache.put("k1", vec![0.0; 100]);
728 let m1 = cache.memory_usage();
729 assert!(m1 > m0);
730 }
731
732 #[test]
735 fn test_default_config() {
736 let c = VectorCacheConfig::default();
737 assert_eq!(c.max_entries, 10_000);
738 assert_eq!(c.max_memory_bytes, 0);
739 assert!(c.default_ttl.is_none());
740 }
741
742 #[test]
743 fn test_default_cache() {
744 let cache = VectorCache::default();
745 assert!(cache.is_empty());
746 }
747
748 #[test]
751 fn test_empty_vector() {
752 let mut cache = VectorCache::new();
753 cache.put("empty", vec![]);
754 let v = cache.get("empty");
755 assert_eq!(v.expect("should exist"), Vec::<f64>::new());
756 }
757
758 #[test]
759 fn test_large_vector() {
760 let mut cache = VectorCache::new();
761 let big = vec![1.0; 10_000];
762 cache.put("big", big.clone());
763 let v = cache.get("big").expect("should exist");
764 assert_eq!(v.len(), 10_000);
765 }
766
767 #[test]
768 fn test_invalidate_prefix_no_match() {
769 let mut cache = VectorCache::new();
770 cache.put("k1", vec3(1.0, 2.0, 3.0));
771 let removed = cache.invalidate_prefix("zzz:");
772 assert_eq!(removed, 0);
773 assert_eq!(cache.len(), 1);
774 }
775
776 #[test]
779 fn test_multiple_evictions() {
780 let config = VectorCacheConfig {
781 max_entries: 3,
782 ..Default::default()
783 };
784 let mut cache = VectorCache::with_config(config);
785 for i in 0..10 {
786 cache.put(format!("k{i}"), vec![i as f64]);
787 }
788 assert_eq!(cache.len(), 3);
789 assert!(cache.statistics().evictions >= 7);
790 }
791
792 #[test]
793 fn test_batch_get_all_miss() {
794 let mut cache = VectorCache::new();
795 let results = cache.batch_get(&["a", "b", "c"]);
796 assert!(results.is_empty());
797 assert_eq!(cache.statistics().misses, 3);
798 }
799
800 #[test]
801 fn test_batch_put_then_invalidate() {
802 let mut cache = VectorCache::new();
803 cache.batch_put(vec![
804 ("a".to_string(), vec3(1.0, 0.0, 0.0)),
805 ("b".to_string(), vec3(0.0, 1.0, 0.0)),
806 ]);
807 cache.invalidate("a");
808 assert!(!cache.contains_key("a"));
809 assert!(cache.contains_key("b"));
810 }
811
812 #[test]
813 fn test_warm_empty_list() {
814 let mut cache = VectorCache::new();
815 let loaded = cache.warm(vec![]);
816 assert_eq!(loaded, 0);
817 }
818
819 #[test]
820 fn test_snapshot_empty_cache() {
821 let cache = VectorCache::new();
822 let snap = cache.snapshot();
823 assert_eq!(snap.entry_count, 0);
824 assert!(snap.entries.is_empty());
825 }
826
827 #[test]
828 fn test_load_snapshot_into_non_empty_cache() {
829 let mut cache1 = VectorCache::new();
830 cache1.put("a", vec3(1.0, 0.0, 0.0));
831 let snap = cache1.snapshot();
832
833 let mut cache2 = VectorCache::new();
834 cache2.put("b", vec3(0.0, 1.0, 0.0));
835 cache2.load_snapshot(snap);
836
837 assert!(cache2.contains_key("a"));
838 assert!(cache2.contains_key("b"));
839 assert_eq!(cache2.len(), 2);
840 }
841
842 #[test]
843 fn test_put_updates_insert_count() {
844 let mut cache = VectorCache::new();
845 cache.put("k1", vec3(1.0, 0.0, 0.0));
846 cache.put("k2", vec3(0.0, 1.0, 0.0));
847 assert_eq!(cache.statistics().inserts, 2);
848 }
849
850 #[test]
851 fn test_clear_resets_len() {
852 let mut cache = VectorCache::new();
853 cache.put("a", vec3(1.0, 0.0, 0.0));
854 cache.put("b", vec3(0.0, 1.0, 0.0));
855 cache.put("c", vec3(0.0, 0.0, 1.0));
856 cache.clear();
857 assert_eq!(cache.len(), 0);
858 assert!(cache.is_empty());
859 }
860
861 #[test]
862 fn test_stats_invalidation_count() {
863 let mut cache = VectorCache::new();
864 cache.put("a", vec3(1.0, 0.0, 0.0));
865 cache.put("b", vec3(0.0, 1.0, 0.0));
866 cache.invalidate("a");
867 cache.invalidate("b");
868 assert_eq!(cache.statistics().invalidations, 2);
869 }
870
871 #[test]
872 fn test_get_after_clear() {
873 let mut cache = VectorCache::new();
874 cache.put("k1", vec3(1.0, 2.0, 3.0));
875 cache.clear();
876 assert!(cache.get("k1").is_none());
877 }
878
879 #[test]
880 fn test_sweep_expired_none_expired() {
881 let mut cache = VectorCache::new();
882 cache.put("k1", vec3(1.0, 2.0, 3.0));
883 let swept = cache.sweep_expired();
884 assert_eq!(swept, 0);
885 assert_eq!(cache.len(), 1);
886 }
887
888 #[test]
889 fn test_contains_key_after_eviction() {
890 let config = VectorCacheConfig {
891 max_entries: 1,
892 ..Default::default()
893 };
894 let mut cache = VectorCache::with_config(config);
895 cache.put("first", vec3(1.0, 0.0, 0.0));
896 cache.put("second", vec3(0.0, 1.0, 0.0));
897 assert!(!cache.contains_key("first"));
898 assert!(cache.contains_key("second"));
899 }
900
901 #[test]
902 fn test_invalidate_prefix_all() {
903 let mut cache = VectorCache::new();
904 cache.put("x:1", vec3(1.0, 0.0, 0.0));
905 cache.put("x:2", vec3(0.0, 1.0, 0.0));
906 cache.put("x:3", vec3(0.0, 0.0, 1.0));
907 let removed = cache.invalidate_prefix("x:");
908 assert_eq!(removed, 3);
909 assert!(cache.is_empty());
910 }
911
912 #[test]
913 fn test_memory_usage_after_clear() {
914 let mut cache = VectorCache::new();
915 cache.put("k1", vec![0.0; 1000]);
916 let before = cache.memory_usage();
917 assert!(before > 0);
918 cache.clear();
919 assert_eq!(cache.memory_usage(), 0);
920 }
921
922 #[test]
923 fn test_put_with_zero_ttl() {
924 let mut cache = VectorCache::new();
925 cache.put_with_ttl("k1", vec3(1.0, 2.0, 3.0), Some(Duration::from_secs(0)));
926 std::thread::sleep(Duration::from_millis(2));
928 assert!(cache.get("k1").is_none());
929 }
930
931 #[test]
932 fn test_lru_order_after_overwrite() {
933 let config = VectorCacheConfig {
934 max_entries: 2,
935 ..Default::default()
936 };
937 let mut cache = VectorCache::with_config(config);
938 cache.put("a", vec3(1.0, 0.0, 0.0));
939 cache.put("b", vec3(0.0, 1.0, 0.0));
940 cache.put("a", vec3(9.0, 9.0, 9.0));
942 cache.put("c", vec3(0.0, 0.0, 1.0));
944 assert!(cache.contains_key("a"));
945 assert!(!cache.contains_key("b"));
946 assert!(cache.contains_key("c"));
947 }
948}