1use serde::{Deserialize, Serialize};
11use std::collections::{HashMap, VecDeque};
12use std::hash::Hash;
13use std::sync::{Arc, RwLock};
14use std::time::{Duration, Instant};
15
16use crate::{Rule, RuleAtom};
17
18#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
20pub struct RuleCacheKey {
21 pub rule_name: String,
22 pub input_facts: Vec<RuleAtom>,
23}
24
25#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
27pub struct DerivationCacheKey {
28 pub goal_fact: RuleAtom,
29 pub context_facts: Vec<RuleAtom>,
30}
31
32#[derive(Debug, Clone)]
34pub struct CacheEntry<T> {
35 pub value: T,
36 pub timestamp: Instant,
37 pub access_count: usize,
38 pub last_access: Instant,
39 pub ttl: Option<Duration>,
40}
41
42#[derive(Debug, Clone, Copy, Serialize, Deserialize)]
44pub enum EvictionPolicy {
45 LRU, LFU, FIFO, TTL, Adaptive, }
51
52#[derive(Debug, Clone)]
54pub struct CacheStatistics {
55 pub hits: usize,
56 pub misses: usize,
57 pub evictions: usize,
58 pub insertions: usize,
59 pub memory_usage: usize,
60 pub hit_rate: f64,
61 pub average_access_time: Duration,
62 pub cache_size: usize,
63 pub max_size: usize,
64}
65
66#[derive(Debug)]
68pub struct SmartCache<K, V>
69where
70 K: Clone + Eq + Hash,
71 V: Clone,
72{
73 entries: HashMap<K, CacheEntry<V>>,
74 access_order: VecDeque<K>,
75 max_size: usize,
76 policy: EvictionPolicy,
77 stats: CacheStatistics,
78 default_ttl: Option<Duration>,
79}
80
81#[derive(Debug)]
83pub struct RuleCache {
84 rule_results: Arc<RwLock<SmartCache<RuleCacheKey, Vec<RuleAtom>>>>,
85 derivation_results: Arc<RwLock<SmartCache<DerivationCacheKey, bool>>>,
86 unification_cache: Arc<RwLock<SmartCache<String, HashMap<String, String>>>>,
87 pattern_cache: Arc<RwLock<SmartCache<String, Vec<RuleAtom>>>>,
88 enabled: bool,
89}
90
91impl<K, V> SmartCache<K, V>
92where
93 K: Clone + Eq + Hash,
94 V: Clone,
95{
96 pub fn new(max_size: usize, policy: EvictionPolicy) -> Self {
98 Self {
99 entries: HashMap::new(),
100 access_order: VecDeque::new(),
101 max_size,
102 policy,
103 stats: CacheStatistics::default(),
104 default_ttl: None,
105 }
106 }
107
108 pub fn with_ttl(max_size: usize, policy: EvictionPolicy, ttl: Duration) -> Self {
110 let mut cache = Self::new(max_size, policy);
111 cache.default_ttl = Some(ttl);
112 cache
113 }
114
115 pub fn get(&mut self, key: &K) -> Option<V> {
117 let now = Instant::now();
118
119 let result = if let Some(entry) = self.entries.get(key) {
121 if let Some(ttl) = entry.ttl {
123 if now.duration_since(entry.timestamp) > ttl {
124 None } else {
126 Some(entry.value.clone())
127 }
128 } else {
129 Some(entry.value.clone())
130 }
131 } else {
132 None
133 };
134
135 match result {
136 Some(value) => {
137 if let Some(entry) = self.entries.get_mut(key) {
139 entry.access_count += 1;
140 entry.last_access = now;
141 }
142
143 self.update_access_order(key);
145
146 self.stats.hits += 1;
147 self.update_hit_rate();
148
149 Some(value)
150 }
151 None => {
152 if self.entries.contains_key(key) {
154 self.entries.remove(key);
155 self.remove_from_access_order(key);
156 }
157
158 self.stats.misses += 1;
159 self.update_hit_rate();
160 None
161 }
162 }
163 }
164
165 pub fn insert(&mut self, key: K, value: V) {
167 self.insert_with_ttl(key, value, self.default_ttl);
168 }
169
170 pub fn insert_with_ttl(&mut self, key: K, value: V, ttl: Option<Duration>) {
172 let now = Instant::now();
173
174 if self.entries.len() >= self.max_size && !self.entries.contains_key(&key) {
176 self.evict();
177 }
178
179 let entry = CacheEntry {
180 value,
181 timestamp: now,
182 access_count: 1,
183 last_access: now,
184 ttl,
185 };
186
187 if self.entries.contains_key(&key) {
189 self.remove_from_access_order(&key);
190 } else {
191 self.stats.insertions += 1;
192 }
193
194 self.entries.insert(key.clone(), entry);
195 self.access_order.push_back(key);
196
197 self.update_memory_usage();
198 }
199
200 pub fn remove(&mut self, key: &K) -> Option<V> {
202 match self.entries.remove(key) {
203 Some(entry) => {
204 self.remove_from_access_order(key);
205 self.update_memory_usage();
206 Some(entry.value)
207 }
208 _ => None,
209 }
210 }
211
212 pub fn clear(&mut self) {
214 self.entries.clear();
215 self.access_order.clear();
216 self.stats = CacheStatistics::default();
217 self.stats.max_size = self.max_size;
218 }
219
220 pub fn stats(&self) -> &CacheStatistics {
222 &self.stats
223 }
224
225 pub fn cleanup_expired(&mut self) {
227 let now = Instant::now();
228 let mut expired_keys = Vec::new();
229
230 for (key, entry) in &self.entries {
231 if let Some(ttl) = entry.ttl {
232 if now.duration_since(entry.timestamp) > ttl {
233 expired_keys.push(key.clone());
234 }
235 }
236 }
237
238 for key in expired_keys {
239 self.remove(&key);
240 }
241 }
242
243 fn evict(&mut self) {
245 if self.entries.is_empty() {
246 return;
247 }
248
249 let key_to_evict = match self.policy {
250 EvictionPolicy::LRU => self.access_order.front().cloned(),
251 EvictionPolicy::LFU => self.find_least_frequently_used(),
252 EvictionPolicy::FIFO => self.access_order.front().cloned(),
253 EvictionPolicy::TTL => self.find_expired_entry(),
254 EvictionPolicy::Adaptive => self.adaptive_eviction(),
255 };
256
257 if let Some(key) = key_to_evict {
258 self.remove(&key);
259 self.stats.evictions += 1;
260 }
261 }
262
263 fn find_least_frequently_used(&self) -> Option<K> {
264 self.entries
265 .iter()
266 .min_by_key(|(_, entry)| entry.access_count)
267 .map(|(key, _)| key.clone())
268 }
269
270 fn find_expired_entry(&self) -> Option<K> {
271 let now = Instant::now();
272 for (key, entry) in &self.entries {
273 if let Some(ttl) = entry.ttl {
274 if now.duration_since(entry.timestamp) > ttl {
275 return Some(key.clone());
276 }
277 }
278 }
279 self.access_order.front().cloned()
281 }
282
283 fn adaptive_eviction(&self) -> Option<K> {
284 let now = Instant::now();
286 let mut best_score = f64::INFINITY;
287 let mut best_key = None;
288
289 for (key, entry) in &self.entries {
290 let recency_score = now.duration_since(entry.last_access).as_secs_f64();
291 let frequency_score = 1.0 / (entry.access_count as f64 + 1.0);
292 let combined_score = recency_score * frequency_score;
293
294 if combined_score < best_score {
295 best_score = combined_score;
296 best_key = Some(key.clone());
297 }
298 }
299
300 best_key
301 }
302
303 fn update_access_order(&mut self, key: &K) {
304 self.remove_from_access_order(key);
305 self.access_order.push_back(key.clone());
306 }
307
308 fn remove_from_access_order(&mut self, key: &K) {
309 if let Some(pos) = self.access_order.iter().position(|x| x == key) {
310 self.access_order.remove(pos);
311 }
312 }
313
314 fn update_hit_rate(&mut self) {
315 let total_requests = self.stats.hits + self.stats.misses;
316 if total_requests > 0 {
317 self.stats.hit_rate = self.stats.hits as f64 / total_requests as f64;
318 }
319 }
320
321 fn update_memory_usage(&mut self) {
322 self.stats.memory_usage = self.entries.len() * 128; self.stats.cache_size = self.entries.len();
324 }
325}
326
327impl RuleCache {
328 pub fn new() -> Self {
330 Self {
331 rule_results: Arc::new(RwLock::new(SmartCache::new(1000, EvictionPolicy::Adaptive))),
332 derivation_results: Arc::new(RwLock::new(SmartCache::new(500, EvictionPolicy::LRU))),
333 unification_cache: Arc::new(RwLock::new(SmartCache::new(200, EvictionPolicy::LFU))),
334 pattern_cache: Arc::new(RwLock::new(SmartCache::with_ttl(
335 300,
336 EvictionPolicy::TTL,
337 Duration::from_secs(300),
338 ))),
339 enabled: true,
340 }
341 }
342
343 pub fn with_sizes(
345 rule_cache_size: usize,
346 derivation_cache_size: usize,
347 unification_cache_size: usize,
348 pattern_cache_size: usize,
349 ) -> Self {
350 Self {
351 rule_results: Arc::new(RwLock::new(SmartCache::new(
352 rule_cache_size,
353 EvictionPolicy::Adaptive,
354 ))),
355 derivation_results: Arc::new(RwLock::new(SmartCache::new(
356 derivation_cache_size,
357 EvictionPolicy::LRU,
358 ))),
359 unification_cache: Arc::new(RwLock::new(SmartCache::new(
360 unification_cache_size,
361 EvictionPolicy::LFU,
362 ))),
363 pattern_cache: Arc::new(RwLock::new(SmartCache::with_ttl(
364 pattern_cache_size,
365 EvictionPolicy::TTL,
366 Duration::from_secs(300),
367 ))),
368 enabled: true,
369 }
370 }
371
372 pub fn set_enabled(&mut self, enabled: bool) {
374 self.enabled = enabled;
375 }
376
377 pub fn cache_rule_result(
379 &self,
380 rule_name: &str,
381 input_facts: &[RuleAtom],
382 result: Vec<RuleAtom>,
383 ) {
384 if !self.enabled {
385 return;
386 }
387
388 let key = RuleCacheKey {
389 rule_name: rule_name.to_string(),
390 input_facts: input_facts.to_vec(),
391 };
392
393 if let Ok(mut cache) = self.rule_results.write() {
394 cache.insert(key, result);
395 }
396 }
397
398 pub fn get_rule_result(
400 &self,
401 rule_name: &str,
402 input_facts: &[RuleAtom],
403 ) -> Option<Vec<RuleAtom>> {
404 if !self.enabled {
405 return None;
406 }
407
408 let key = RuleCacheKey {
409 rule_name: rule_name.to_string(),
410 input_facts: input_facts.to_vec(),
411 };
412
413 match self.rule_results.write() {
414 Ok(mut cache) => cache.get(&key),
415 _ => None,
416 }
417 }
418
419 pub fn cache_derivation(&self, goal: &RuleAtom, context: &[RuleAtom], result: bool) {
421 if !self.enabled {
422 return;
423 }
424
425 let key = DerivationCacheKey {
426 goal_fact: goal.clone(),
427 context_facts: context.to_vec(),
428 };
429
430 if let Ok(mut cache) = self.derivation_results.write() {
431 cache.insert(key, result);
432 }
433 }
434
435 pub fn get_derivation(&self, goal: &RuleAtom, context: &[RuleAtom]) -> Option<bool> {
437 if !self.enabled {
438 return None;
439 }
440
441 let key = DerivationCacheKey {
442 goal_fact: goal.clone(),
443 context_facts: context.to_vec(),
444 };
445
446 match self.derivation_results.write() {
447 Ok(mut cache) => cache.get(&key),
448 _ => None,
449 }
450 }
451
452 pub fn cache_unification(&self, pattern: &str, bindings: HashMap<String, String>) {
454 if !self.enabled {
455 return;
456 }
457
458 if let Ok(mut cache) = self.unification_cache.write() {
459 cache.insert(pattern.to_string(), bindings);
460 }
461 }
462
463 pub fn get_unification(&self, pattern: &str) -> Option<HashMap<String, String>> {
465 if !self.enabled {
466 return None;
467 }
468
469 match self.unification_cache.write() {
470 Ok(mut cache) => cache.get(&pattern.to_string()),
471 _ => None,
472 }
473 }
474
475 pub fn cache_pattern(&self, pattern: &str, matches: Vec<RuleAtom>) {
477 if !self.enabled {
478 return;
479 }
480
481 if let Ok(mut cache) = self.pattern_cache.write() {
482 cache.insert(pattern.to_string(), matches);
483 }
484 }
485
486 pub fn get_pattern(&self, pattern: &str) -> Option<Vec<RuleAtom>> {
488 if !self.enabled {
489 return None;
490 }
491
492 match self.pattern_cache.write() {
493 Ok(mut cache) => cache.get(&pattern.to_string()),
494 _ => None,
495 }
496 }
497
498 pub fn clear_all(&self) {
500 if let Ok(mut cache) = self.rule_results.write() {
501 cache.clear();
502 }
503 if let Ok(mut cache) = self.derivation_results.write() {
504 cache.clear();
505 }
506 if let Ok(mut cache) = self.unification_cache.write() {
507 cache.clear();
508 }
509 if let Ok(mut cache) = self.pattern_cache.write() {
510 cache.clear();
511 }
512 }
513
514 pub fn cleanup_expired(&self) {
516 if let Ok(mut cache) = self.rule_results.write() {
517 cache.cleanup_expired();
518 }
519 if let Ok(mut cache) = self.derivation_results.write() {
520 cache.cleanup_expired();
521 }
522 if let Ok(mut cache) = self.unification_cache.write() {
523 cache.cleanup_expired();
524 }
525 if let Ok(mut cache) = self.pattern_cache.write() {
526 cache.cleanup_expired();
527 }
528 }
529
530 pub fn get_statistics(&self) -> CachingStatistics {
532 let rule_stats = match self.rule_results.read() {
533 Ok(cache) => cache.stats().clone(),
534 _ => CacheStatistics::default(),
535 };
536
537 let derivation_stats = match self.derivation_results.read() {
538 Ok(cache) => cache.stats().clone(),
539 _ => CacheStatistics::default(),
540 };
541
542 let unification_stats = match self.unification_cache.read() {
543 Ok(cache) => cache.stats().clone(),
544 _ => CacheStatistics::default(),
545 };
546
547 let pattern_stats = match self.pattern_cache.read() {
548 Ok(cache) => cache.stats().clone(),
549 _ => CacheStatistics::default(),
550 };
551
552 CachingStatistics {
553 rule_cache: rule_stats,
554 derivation_cache: derivation_stats,
555 unification_cache: unification_stats,
556 pattern_cache: pattern_stats,
557 enabled: self.enabled,
558 }
559 }
560
561 pub fn warm_cache(&self, rules: &[Rule], common_facts: &[RuleAtom]) {
563 if !self.enabled {
564 return;
565 }
566
567 for rule in rules {
569 for atom in &rule.body {
570 let pattern = format!("{atom:?}");
571 self.cache_pattern(&pattern, vec![atom.clone()]);
572 }
573 }
574
575 for fact in common_facts {
577 let pattern = format!("{fact:?}");
578 self.cache_pattern(&pattern, vec![fact.clone()]);
579 }
580 }
581}
582
583#[derive(Debug, Clone)]
585pub struct CachingStatistics {
586 pub rule_cache: CacheStatistics,
587 pub derivation_cache: CacheStatistics,
588 pub unification_cache: CacheStatistics,
589 pub pattern_cache: CacheStatistics,
590 pub enabled: bool,
591}
592
593impl Default for CacheStatistics {
594 fn default() -> Self {
595 Self {
596 hits: 0,
597 misses: 0,
598 evictions: 0,
599 insertions: 0,
600 memory_usage: 0,
601 hit_rate: 0.0,
602 average_access_time: Duration::from_nanos(0),
603 cache_size: 0,
604 max_size: 0,
605 }
606 }
607}
608
609impl Default for RuleCache {
610 fn default() -> Self {
611 Self::new()
612 }
613}
614
615#[cfg(test)]
616mod tests {
617 use super::*;
618 use crate::Term;
619
620 #[test]
621 fn test_smart_cache_basic_operations() {
622 let mut cache = SmartCache::new(3, EvictionPolicy::LRU);
623
624 cache.insert("key1".to_string(), "value1".to_string());
625 cache.insert("key2".to_string(), "value2".to_string());
626
627 assert_eq!(cache.get(&"key1".to_string()), Some("value1".to_string()));
628 assert_eq!(cache.get(&"key2".to_string()), Some("value2".to_string()));
629 assert_eq!(cache.get(&"key3".to_string()), None);
630 }
631
632 #[test]
633 fn test_cache_eviction() {
634 let mut cache = SmartCache::new(2, EvictionPolicy::LRU);
635
636 cache.insert("key1", "value1");
637 cache.insert("key2", "value2");
638 cache.insert("key3", "value3"); assert_eq!(cache.get(&"key1"), None);
641 assert_eq!(cache.get(&"key2"), Some("value2"));
642 assert_eq!(cache.get(&"key3"), Some("value3"));
643 }
644
645 #[test]
646 fn test_ttl_expiration() {
647 let mut cache = SmartCache::with_ttl(5, EvictionPolicy::TTL, Duration::from_millis(10));
648
649 cache.insert("key1", "value1");
650 assert_eq!(cache.get(&"key1"), Some("value1"));
651
652 std::thread::sleep(Duration::from_millis(15));
653 assert_eq!(cache.get(&"key1"), None);
654 }
655
656 #[test]
657 fn test_rule_cache_operations() {
658 let cache = RuleCache::new();
659
660 let rule_name = "test_rule";
661 let input_facts = vec![RuleAtom::Triple {
662 subject: Term::Constant("test".to_string()),
663 predicate: Term::Constant("type".to_string()),
664 object: Term::Constant("entity".to_string()),
665 }];
666 let result = vec![RuleAtom::Triple {
667 subject: Term::Constant("test".to_string()),
668 predicate: Term::Constant("derived".to_string()),
669 object: Term::Constant("property".to_string()),
670 }];
671
672 cache.cache_rule_result(rule_name, &input_facts, result.clone());
674 let cached_result = cache.get_rule_result(rule_name, &input_facts);
675
676 assert_eq!(cached_result, Some(result));
677 }
678
679 #[test]
680 fn test_cache_statistics() {
681 let mut cache = SmartCache::new(10, EvictionPolicy::LRU);
682
683 cache.insert("key1", "value1");
685 cache.insert("key2", "value2");
686 cache.get(&"key1");
687 cache.get(&"key3"); let stats = cache.stats();
690 assert_eq!(stats.hits, 1);
691 assert_eq!(stats.misses, 1);
692 assert_eq!(stats.insertions, 2);
693 assert!(stats.hit_rate > 0.0);
694 }
695
696 #[test]
697 fn test_cache_warm_up() {
698 let cache = RuleCache::new();
699
700 let rules = vec![Rule {
701 name: "test_rule".to_string(),
702 body: vec![RuleAtom::Triple {
703 subject: Term::Variable("X".to_string()),
704 predicate: Term::Constant("type".to_string()),
705 object: Term::Constant("test".to_string()),
706 }],
707 head: vec![RuleAtom::Triple {
708 subject: Term::Variable("X".to_string()),
709 predicate: Term::Constant("derived".to_string()),
710 object: Term::Constant("property".to_string()),
711 }],
712 }];
713
714 let facts = vec![RuleAtom::Triple {
715 subject: Term::Constant("entity1".to_string()),
716 predicate: Term::Constant("type".to_string()),
717 object: Term::Constant("test".to_string()),
718 }];
719
720 cache.warm_cache(&rules, &facts);
721
722 let stats = cache.get_statistics();
724 assert!(stats.pattern_cache.cache_size > 0);
725 }
726}