1use crate::metamodel::{Aspect, Characteristic, Entity, Operation, Property};
36use std::collections::HashMap;
37use std::sync::{Arc, RwLock};
38
39#[derive(Clone)]
41struct CacheEntry<T> {
42 value: Arc<T>,
43 access_count: usize,
44 last_accessed: std::time::Instant,
45}
46
47pub struct LruModelCache<T> {
51 capacity: usize,
52 entries: Arc<RwLock<HashMap<String, CacheEntry<T>>>>,
53 access_order: Arc<RwLock<Vec<String>>>,
54 hits: Arc<RwLock<usize>>,
55 misses: Arc<RwLock<usize>>,
56}
57
58impl<T> LruModelCache<T>
59where
60 T: Clone,
61{
62 pub fn new(capacity: usize) -> Self {
78 Self {
79 capacity: capacity.max(1), entries: Arc::new(RwLock::new(HashMap::new())),
81 access_order: Arc::new(RwLock::new(Vec::new())),
82 hits: Arc::new(RwLock::new(0)),
83 misses: Arc::new(RwLock::new(0)),
84 }
85 }
86
87 pub fn get(&self, key: &str) -> Option<Arc<T>> {
91 let mut entries = self.entries.write().expect("lock poisoned");
92
93 if let Some(entry) = entries.get_mut(key) {
94 entry.access_count += 1;
96 entry.last_accessed = std::time::Instant::now();
97
98 let mut access_order = self.access_order.write().expect("lock poisoned");
100 if let Some(pos) = access_order.iter().position(|k| k == key) {
101 access_order.remove(pos);
102 }
103 access_order.push(key.to_string());
104
105 *self.hits.write().expect("lock poisoned") += 1;
107
108 Some(Arc::clone(&entry.value))
109 } else {
110 *self.misses.write().expect("lock poisoned") += 1;
112 None
113 }
114 }
115
116 pub fn put(&mut self, key: String, value: Arc<T>) {
120 let mut entries = self.entries.write().expect("lock poisoned");
121 let mut access_order = self.access_order.write().expect("lock poisoned");
122
123 if entries.contains_key(&key) {
125 if let Some(pos) = access_order.iter().position(|k| k == &key) {
126 access_order.remove(pos);
127 }
128 }
129
130 if entries.len() >= self.capacity && !entries.contains_key(&key) {
132 if let Some(lru_key) = access_order.first() {
133 let lru_key = lru_key.clone();
134 entries.remove(&lru_key);
135 access_order.remove(0);
136 }
137 }
138
139 let entry = CacheEntry {
141 value,
142 access_count: 0,
143 last_accessed: std::time::Instant::now(),
144 };
145
146 entries.insert(key.clone(), entry);
147 access_order.push(key);
148 }
149
150 pub fn contains(&self, key: &str) -> bool {
152 let entries = self.entries.read().expect("lock poisoned");
153 entries.contains_key(key)
154 }
155
156 pub fn remove(&mut self, key: &str) -> Option<Arc<T>> {
158 let mut entries = self.entries.write().expect("lock poisoned");
159 let mut access_order = self.access_order.write().expect("lock poisoned");
160
161 if let Some(pos) = access_order.iter().position(|k| k == key) {
162 access_order.remove(pos);
163 }
164
165 entries.remove(key).map(|entry| entry.value)
166 }
167
168 pub fn clear(&mut self) {
170 let mut entries = self.entries.write().expect("lock poisoned");
171 let mut access_order = self.access_order.write().expect("lock poisoned");
172
173 entries.clear();
174 access_order.clear();
175 }
176
177 pub fn len(&self) -> usize {
179 let entries = self.entries.read().expect("lock poisoned");
180 entries.len()
181 }
182
183 pub fn is_empty(&self) -> bool {
185 self.len() == 0
186 }
187
188 pub fn capacity(&self) -> usize {
190 self.capacity
191 }
192
193 pub fn resize(&mut self, new_capacity: usize) {
197 self.capacity = new_capacity.max(1);
198
199 let mut entries = self.entries.write().expect("lock poisoned");
200 let mut access_order = self.access_order.write().expect("lock poisoned");
201
202 while entries.len() > self.capacity {
204 if let Some(lru_key) = access_order.first() {
205 let lru_key = lru_key.clone();
206 entries.remove(&lru_key);
207 access_order.remove(0);
208 } else {
209 break;
210 }
211 }
212 }
213
214 pub fn hit_rate(&self) -> f64 {
216 let hits = *self.hits.read().expect("lock poisoned");
217 let misses = *self.misses.read().expect("lock poisoned");
218 let total = hits + misses;
219
220 if total == 0 {
221 0.0
222 } else {
223 hits as f64 / total as f64
224 }
225 }
226
227 pub fn hits(&self) -> usize {
229 *self.hits.read().expect("lock poisoned")
230 }
231
232 pub fn misses(&self) -> usize {
234 *self.misses.read().expect("lock poisoned")
235 }
236
237 pub fn reset_statistics(&mut self) {
239 *self.hits.write().expect("lock poisoned") = 0;
240 *self.misses.write().expect("lock poisoned") = 0;
241 }
242
243 pub fn keys(&self) -> Vec<String> {
245 let access_order = self.access_order.read().expect("lock poisoned");
246 access_order.clone()
247 }
248
249 pub fn statistics(&self) -> CacheStatistics {
251 CacheStatistics {
252 size: self.len(),
253 capacity: self.capacity,
254 hits: self.hits(),
255 misses: self.misses(),
256 hit_rate: self.hit_rate(),
257 }
258 }
259}
260
261impl<T> Clone for LruModelCache<T> {
262 fn clone(&self) -> Self {
263 Self {
264 capacity: self.capacity,
265 entries: Arc::clone(&self.entries),
266 access_order: Arc::clone(&self.access_order),
267 hits: Arc::clone(&self.hits),
268 misses: Arc::clone(&self.misses),
269 }
270 }
271}
272
273#[derive(Debug, Clone)]
275pub struct CacheStatistics {
276 pub size: usize,
278 pub capacity: usize,
280 pub hits: usize,
282 pub misses: usize,
284 pub hit_rate: f64,
286}
287
288impl CacheStatistics {
289 pub fn fill_percentage(&self) -> f64 {
291 if self.capacity == 0 {
292 0.0
293 } else {
294 (self.size as f64 / self.capacity as f64) * 100.0
295 }
296 }
297
298 pub fn total_accesses(&self) -> usize {
300 self.hits + self.misses
301 }
302}
303
304pub type AspectCache = LruModelCache<Aspect>;
307
308pub type PropertyCache = LruModelCache<Property>;
310
311pub type CharacteristicCache = LruModelCache<Characteristic>;
313
314pub type EntityCache = LruModelCache<Entity>;
316
317pub type OperationCache = LruModelCache<Operation>;
319
320#[derive(Clone)]
322struct TtlCacheEntry<T> {
323 value: Arc<T>,
324 access_count: usize,
325 created_at: std::time::Instant,
326 expires_at: std::time::Instant,
327}
328
329pub struct TtlCache<T> {
359 capacity: usize,
360 ttl: std::time::Duration,
361 entries: Arc<RwLock<HashMap<String, TtlCacheEntry<T>>>>,
362 access_order: Arc<RwLock<Vec<String>>>,
363 hits: Arc<RwLock<usize>>,
364 misses: Arc<RwLock<usize>>,
365 expirations: Arc<RwLock<usize>>,
366}
367
368impl<T> TtlCache<T>
369where
370 T: Clone,
371{
372 pub fn new(capacity: usize, ttl: std::time::Duration) -> Self {
390 Self {
391 capacity: capacity.max(1),
392 ttl,
393 entries: Arc::new(RwLock::new(HashMap::new())),
394 access_order: Arc::new(RwLock::new(Vec::new())),
395 hits: Arc::new(RwLock::new(0)),
396 misses: Arc::new(RwLock::new(0)),
397 expirations: Arc::new(RwLock::new(0)),
398 }
399 }
400
401 pub fn get(&self, key: &str) -> Option<Arc<T>> {
405 let mut entries = self.entries.write().expect("lock poisoned");
406
407 if let Some(entry) = entries.get_mut(key) {
408 let now = std::time::Instant::now();
409
410 if now >= entry.expires_at {
412 entries.remove(key);
414 let mut access_order = self.access_order.write().expect("lock poisoned");
415 if let Some(pos) = access_order.iter().position(|k| k == key) {
416 access_order.remove(pos);
417 }
418 *self.expirations.write().expect("lock poisoned") += 1;
419 *self.misses.write().expect("lock poisoned") += 1;
420 return None;
421 }
422
423 entry.access_count += 1;
425
426 let mut access_order = self.access_order.write().expect("lock poisoned");
428 if let Some(pos) = access_order.iter().position(|k| k == key) {
429 access_order.remove(pos);
430 }
431 access_order.push(key.to_string());
432
433 *self.hits.write().expect("lock poisoned") += 1;
435
436 Some(Arc::clone(&entry.value))
437 } else {
438 *self.misses.write().expect("lock poisoned") += 1;
440 None
441 }
442 }
443
444 pub fn put(&mut self, key: String, value: Arc<T>) {
448 let mut entries = self.entries.write().expect("lock poisoned");
449 let mut access_order = self.access_order.write().expect("lock poisoned");
450
451 if entries.contains_key(&key) {
453 if let Some(pos) = access_order.iter().position(|k| k == &key) {
454 access_order.remove(pos);
455 }
456 }
457
458 if entries.len() >= self.capacity && !entries.contains_key(&key) {
460 if let Some(lru_key) = access_order.first() {
461 let lru_key = lru_key.clone();
462 entries.remove(&lru_key);
463 access_order.remove(0);
464 }
465 }
466
467 let now = std::time::Instant::now();
469 let entry = TtlCacheEntry {
470 value,
471 access_count: 0,
472 created_at: now,
473 expires_at: now + self.ttl,
474 };
475
476 entries.insert(key.clone(), entry);
477 access_order.push(key);
478 }
479
480 pub fn evict_expired(&mut self) -> usize {
484 let mut entries = self.entries.write().expect("lock poisoned");
485 let mut access_order = self.access_order.write().expect("lock poisoned");
486 let now = std::time::Instant::now();
487
488 let expired_keys: Vec<String> = entries
489 .iter()
490 .filter(|(_, entry)| now >= entry.expires_at)
491 .map(|(key, _)| key.clone())
492 .collect();
493
494 let count = expired_keys.len();
495
496 for key in expired_keys {
497 entries.remove(&key);
498 if let Some(pos) = access_order.iter().position(|k| k == &key) {
499 access_order.remove(pos);
500 }
501 }
502
503 *self.expirations.write().expect("lock poisoned") += count;
504 count
505 }
506
507 pub fn len(&self) -> usize {
509 let entries = self.entries.read().expect("lock poisoned");
510 entries.len()
511 }
512
513 pub fn is_empty(&self) -> bool {
515 self.len() == 0
516 }
517
518 pub fn capacity(&self) -> usize {
520 self.capacity
521 }
522
523 pub fn ttl(&self) -> std::time::Duration {
525 self.ttl
526 }
527
528 pub fn clear(&mut self) {
530 let mut entries = self.entries.write().expect("lock poisoned");
531 let mut access_order = self.access_order.write().expect("lock poisoned");
532
533 entries.clear();
534 access_order.clear();
535 }
536
537 pub fn hit_rate(&self) -> f64 {
539 let hits = *self.hits.read().expect("lock poisoned");
540 let misses = *self.misses.read().expect("lock poisoned");
541 let total = hits + misses;
542
543 if total == 0 {
544 0.0
545 } else {
546 hits as f64 / total as f64
547 }
548 }
549
550 pub fn expirations(&self) -> usize {
552 *self.expirations.read().expect("lock poisoned")
553 }
554
555 pub fn statistics(&self) -> TtlCacheStatistics {
557 TtlCacheStatistics {
558 size: self.len(),
559 capacity: self.capacity,
560 hits: *self.hits.read().expect("lock poisoned"),
561 misses: *self.misses.read().expect("lock poisoned"),
562 expirations: self.expirations(),
563 hit_rate: self.hit_rate(),
564 ttl_seconds: self.ttl.as_secs(),
565 }
566 }
567}
568
569#[derive(Debug, Clone)]
571pub struct TtlCacheStatistics {
572 pub size: usize,
574 pub capacity: usize,
576 pub hits: usize,
578 pub misses: usize,
580 pub expirations: usize,
582 pub hit_rate: f64,
584 pub ttl_seconds: u64,
586}
587
588impl TtlCacheStatistics {
589 pub fn fill_percentage(&self) -> f64 {
591 if self.capacity == 0 {
592 0.0
593 } else {
594 (self.size as f64 / self.capacity as f64) * 100.0
595 }
596 }
597
598 pub fn total_accesses(&self) -> usize {
600 self.hits + self.misses
601 }
602}
603
604#[cfg(test)]
605mod tests {
606 use super::*;
607 use crate::metamodel::ElementMetadata;
608
609 #[test]
610 fn test_cache_creation() {
611 let cache: LruModelCache<Aspect> = LruModelCache::new(10);
612 assert_eq!(cache.capacity(), 10);
613 assert_eq!(cache.len(), 0);
614 assert!(cache.is_empty());
615 }
616
617 #[test]
618 fn test_put_and_get() {
619 let mut cache = LruModelCache::new(5);
620 let aspect = Arc::new(Aspect::new("urn:test:1.0.0#Test".to_string()));
621
622 cache.put("test".to_string(), Arc::clone(&aspect));
623 assert_eq!(cache.len(), 1);
624
625 let retrieved = cache.get("test");
626 assert!(retrieved.is_some());
627 }
628
629 #[test]
630 fn test_lru_eviction() {
631 let mut cache = LruModelCache::new(3);
632
633 cache.put(
635 "a".to_string(),
636 Arc::new(Aspect::new("urn:test:1.0.0#A".to_string())),
637 );
638 cache.put(
639 "b".to_string(),
640 Arc::new(Aspect::new("urn:test:1.0.0#B".to_string())),
641 );
642 cache.put(
643 "c".to_string(),
644 Arc::new(Aspect::new("urn:test:1.0.0#C".to_string())),
645 );
646
647 assert_eq!(cache.len(), 3);
648
649 cache.put(
651 "d".to_string(),
652 Arc::new(Aspect::new("urn:test:1.0.0#D".to_string())),
653 );
654
655 assert_eq!(cache.len(), 3);
656 assert!(!cache.contains("a")); assert!(cache.contains("b"));
658 assert!(cache.contains("c"));
659 assert!(cache.contains("d"));
660 }
661
662 #[test]
663 fn test_lru_access_updates() {
664 let mut cache = LruModelCache::new(3);
665
666 cache.put(
667 "a".to_string(),
668 Arc::new(Aspect::new("urn:test:1.0.0#A".to_string())),
669 );
670 cache.put(
671 "b".to_string(),
672 Arc::new(Aspect::new("urn:test:1.0.0#B".to_string())),
673 );
674 cache.put(
675 "c".to_string(),
676 Arc::new(Aspect::new("urn:test:1.0.0#C".to_string())),
677 );
678
679 cache.get("a");
681
682 cache.put(
684 "d".to_string(),
685 Arc::new(Aspect::new("urn:test:1.0.0#D".to_string())),
686 );
687
688 assert!(cache.contains("a")); assert!(!cache.contains("b")); assert!(cache.contains("c"));
691 assert!(cache.contains("d"));
692 }
693
694 #[test]
695 fn test_remove() {
696 let mut cache = LruModelCache::new(5);
697 cache.put(
698 "test".to_string(),
699 Arc::new(Aspect::new("urn:test:1.0.0#Test".to_string())),
700 );
701
702 assert_eq!(cache.len(), 1);
703 let removed = cache.remove("test");
704 assert!(removed.is_some());
705 assert_eq!(cache.len(), 0);
706 }
707
708 #[test]
709 fn test_clear() {
710 let mut cache = LruModelCache::new(5);
711 cache.put(
712 "a".to_string(),
713 Arc::new(Aspect::new("urn:test:1.0.0#A".to_string())),
714 );
715 cache.put(
716 "b".to_string(),
717 Arc::new(Aspect::new("urn:test:1.0.0#B".to_string())),
718 );
719
720 assert_eq!(cache.len(), 2);
721 cache.clear();
722 assert_eq!(cache.len(), 0);
723 assert!(cache.is_empty());
724 }
725
726 #[test]
727 fn test_hit_rate() {
728 let mut cache = LruModelCache::new(5);
729 cache.put(
730 "test".to_string(),
731 Arc::new(Aspect::new("urn:test:1.0.0#Test".to_string())),
732 );
733
734 cache.get("test");
736 cache.get("test");
737
738 cache.get("nonexistent");
740
741 assert_eq!(cache.hits(), 2);
742 assert_eq!(cache.misses(), 1);
743 assert!((cache.hit_rate() - 0.666).abs() < 0.01);
744 }
745
746 #[test]
747 fn test_resize() {
748 let mut cache = LruModelCache::new(5);
749
750 for i in 0..5 {
752 cache.put(
753 format!("item{}", i),
754 Arc::new(Aspect::new(format!("urn:test:1.0.0#Item{}", i))),
755 );
756 }
757
758 assert_eq!(cache.len(), 5);
759
760 cache.resize(3);
762 assert_eq!(cache.capacity(), 3);
763 assert_eq!(cache.len(), 3);
764
765 assert!(!cache.contains("item0"));
767 assert!(!cache.contains("item1"));
768 assert!(cache.contains("item2"));
769 assert!(cache.contains("item3"));
770 assert!(cache.contains("item4"));
771 }
772
773 #[test]
774 fn test_statistics() {
775 let mut cache = LruModelCache::new(10);
776 cache.put(
777 "test".to_string(),
778 Arc::new(Aspect::new("urn:test:1.0.0#Test".to_string())),
779 );
780
781 cache.get("test");
782 cache.get("test");
783 cache.get("nonexistent");
784
785 let stats = cache.statistics();
786 assert_eq!(stats.size, 1);
787 assert_eq!(stats.capacity, 10);
788 assert_eq!(stats.hits, 2);
789 assert_eq!(stats.misses, 1);
790 assert_eq!(stats.total_accesses(), 3);
791 assert_eq!(stats.fill_percentage(), 10.0);
792 }
793
794 #[test]
795 fn test_keys() {
796 let mut cache = LruModelCache::new(5);
797 cache.put(
798 "a".to_string(),
799 Arc::new(Aspect::new("urn:test:1.0.0#A".to_string())),
800 );
801 cache.put(
802 "b".to_string(),
803 Arc::new(Aspect::new("urn:test:1.0.0#B".to_string())),
804 );
805 cache.put(
806 "c".to_string(),
807 Arc::new(Aspect::new("urn:test:1.0.0#C".to_string())),
808 );
809
810 let keys = cache.keys();
811 assert_eq!(keys.len(), 3);
812 assert!(keys.contains(&"a".to_string()));
813 assert!(keys.contains(&"b".to_string()));
814 assert!(keys.contains(&"c".to_string()));
815 }
816
817 #[test]
820 fn test_ttl_cache_creation() {
821 let cache: TtlCache<Aspect> = TtlCache::new(10, std::time::Duration::from_secs(60));
822 assert_eq!(cache.capacity(), 10);
823 assert_eq!(cache.len(), 0);
824 assert!(cache.is_empty());
825 assert_eq!(cache.ttl().as_secs(), 60);
826 }
827
828 #[test]
829 fn test_ttl_cache_put_and_get() {
830 let mut cache = TtlCache::new(5, std::time::Duration::from_secs(60));
831 let aspect = Arc::new(Aspect::new("urn:test:1.0.0#Test".to_string()));
832
833 cache.put("test".to_string(), Arc::clone(&aspect));
834 assert_eq!(cache.len(), 1);
835
836 let retrieved = cache.get("test");
837 assert!(retrieved.is_some());
838 }
839
840 #[test]
841 fn test_ttl_cache_expiration() {
842 let mut cache = TtlCache::new(5, std::time::Duration::from_millis(50));
843 let aspect = Arc::new(Aspect::new("urn:test:1.0.0#Test".to_string()));
844
845 cache.put("test".to_string(), Arc::clone(&aspect));
846 assert_eq!(cache.len(), 1);
847
848 assert!(cache.get("test").is_some());
850
851 std::thread::sleep(std::time::Duration::from_millis(100));
853
854 assert!(cache.get("test").is_none());
856 assert_eq!(cache.expirations(), 1);
857 }
858
859 #[test]
860 fn test_ttl_cache_evict_expired() {
861 let mut cache = TtlCache::new(5, std::time::Duration::from_millis(50));
862
863 for i in 0..3 {
865 cache.put(
866 format!("item{}", i),
867 Arc::new(Aspect::new(format!("urn:test:1.0.0#Item{}", i))),
868 );
869 }
870
871 assert_eq!(cache.len(), 3);
872
873 std::thread::sleep(std::time::Duration::from_millis(100));
875
876 let evicted = cache.evict_expired();
878 assert_eq!(evicted, 3);
879 assert_eq!(cache.len(), 0);
880 }
881
882 #[test]
883 fn test_ttl_cache_statistics() {
884 let mut cache = TtlCache::new(10, std::time::Duration::from_secs(60));
885 cache.put(
886 "test".to_string(),
887 Arc::new(Aspect::new("urn:test:1.0.0#Test".to_string())),
888 );
889
890 cache.get("test");
891 cache.get("test");
892 cache.get("nonexistent");
893
894 let stats = cache.statistics();
895 assert_eq!(stats.size, 1);
896 assert_eq!(stats.capacity, 10);
897 assert_eq!(stats.hits, 2);
898 assert_eq!(stats.misses, 1);
899 assert_eq!(stats.total_accesses(), 3);
900 assert_eq!(stats.ttl_seconds, 60);
901 }
902
903 #[test]
904 fn test_ttl_cache_clear() {
905 let mut cache = TtlCache::new(5, std::time::Duration::from_secs(60));
906 cache.put(
907 "a".to_string(),
908 Arc::new(Aspect::new("urn:test:1.0.0#A".to_string())),
909 );
910 cache.put(
911 "b".to_string(),
912 Arc::new(Aspect::new("urn:test:1.0.0#B".to_string())),
913 );
914
915 assert_eq!(cache.len(), 2);
916 cache.clear();
917 assert_eq!(cache.len(), 0);
918 assert!(cache.is_empty());
919 }
920
921 #[test]
922 fn test_ttl_cache_lru_eviction() {
923 let mut cache = TtlCache::new(3, std::time::Duration::from_secs(60));
924
925 cache.put(
927 "a".to_string(),
928 Arc::new(Aspect::new("urn:test:1.0.0#A".to_string())),
929 );
930 cache.put(
931 "b".to_string(),
932 Arc::new(Aspect::new("urn:test:1.0.0#B".to_string())),
933 );
934 cache.put(
935 "c".to_string(),
936 Arc::new(Aspect::new("urn:test:1.0.0#C".to_string())),
937 );
938
939 assert_eq!(cache.len(), 3);
940
941 cache.put(
943 "d".to_string(),
944 Arc::new(Aspect::new("urn:test:1.0.0#D".to_string())),
945 );
946
947 assert_eq!(cache.len(), 3);
948 }
951}