(&self, incoming_size: usize)
| 581 | } |
| 582 | |
| 583 | fn evict_if_needed(&self, incoming_size: usize) { |
| 584 | let current_memory = *self.current_memory.read().unwrap(); |
| 585 | let current_entries = self.entries.read().unwrap().len(); |
| 586 | |
| 587 | if current_memory + incoming_size > self.max_memory_bytes |
| 588 | || current_entries >= self.max_entries |
| 589 | { |
| 590 | // Collect eviction candidates (prioritize by hit rate vs complexity) |
| 591 | let mut candidates: Vec<(SubqueryCacheKey, f64)> = { |
| 592 | let entries = self.entries.read().unwrap(); |
| 593 | entries |
| 594 | .iter() |
| 595 | .map(|(key, entry)| { |
| 596 | // Score based on hit rate, recency, and complexity |
| 597 | let hit_rate = if entry.metadata.access_count == 0 { |
| 598 | 0.0 |
| 599 | } else { |
| 600 | entry.hit_count as f64 / entry.metadata.access_count as f64 |
| 601 | }; |
| 602 | let recency_score = 1.0 / (1.0 + entry.last_hit.elapsed().as_secs() as f64); |
| 603 | let complexity_bonus = entry.complexity_score / 10.0; // Higher complexity = keep longer |
| 604 | |
| 605 | let keep_score = hit_rate + recency_score + complexity_bonus; |
| 606 | (key.clone(), keep_score) |
| 607 | }) |
| 608 | .collect() |
| 609 | }; |
| 610 | |
| 611 | // Sort by keep score (ascending = evict first) |
| 612 | candidates.sort_by(|a, b| a.1.partial_cmp(&b.1).unwrap()); |
| 613 | |
| 614 | // Evict lowest scoring entries |
| 615 | let mut entries = self.entries.write().unwrap(); |
| 616 | for (key, _) in candidates { |
| 617 | if let Some(evicted_entry) = entries.remove(&key) { |
| 618 | let evicted_size = evicted_entry.size_bytes(); |
| 619 | self.update_indices(&key, false); |
| 620 | |
| 621 | { |
| 622 | let mut current_memory = self.current_memory.write().unwrap(); |
| 623 | *current_memory = current_memory.saturating_sub(evicted_size); |
| 624 | } |
| 625 | |
| 626 | // Check if we have enough space now |
| 627 | let new_current_memory = *self.current_memory.read().unwrap(); |
| 628 | let new_current_entries = entries.len(); |
| 629 | |
| 630 | if new_current_memory + incoming_size <= self.max_memory_bytes |
| 631 | && new_current_entries < self.max_entries |
| 632 | { |
| 633 | break; |
| 634 | } |
| 635 | } |
| 636 | } |
| 637 | } |
| 638 | } |
| 639 | |
| 640 | fn update_indices(&self, key: &SubqueryCacheKey, add: bool) { |
no test coverage detected