-
Notifications
You must be signed in to change notification settings - Fork 3
fix(engine): add cache size limits to prevent unbounded memory growth #45
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Greptile OverviewGreptile SummaryAdded Key Changes:
Issues Found:
Confidence Score: 3/5
|
| Filename | Overview |
|---|---|
| src/cortex-engine/src/config/config_discovery.rs | Added MAX_CACHE_SIZE limit and insert_with_eviction helper, but eviction strategy is non-deterministic |
| src/cortex-engine/src/tokenizer.rs | Added MAX_CACHE_SIZE limit and insert_with_eviction helper with same non-deterministic eviction issue |
Sequence Diagram
sequenceDiagram
participant Client
participant find_up/count
participant Cache as HashMap Cache
participant insert_with_eviction
Client->>find_up/count: Request config/token count
find_up/count->>Cache: Check cache (read lock)
alt Cache hit
Cache-->>find_up/count: Return cached value
find_up/count-->>Client: Return result
else Cache miss
find_up/count->>find_up/count: Compute result
find_up/count->>Cache: Acquire write lock
find_up/count->>insert_with_eviction: Insert with eviction
alt Cache at MAX_CACHE_SIZE
insert_with_eviction->>Cache: keys().next()
Cache-->>insert_with_eviction: Arbitrary key
insert_with_eviction->>Cache: Remove arbitrary entry
end
insert_with_eviction->>Cache: Insert new entry
Cache-->>find_up/count: Release lock
find_up/count-->>Client: Return result
end
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
2 files reviewed, 4 comments
| fn insert_with_eviction<K: Eq + Hash + Clone, V>(cache: &mut HashMap<K, V>, key: K, value: V) { | ||
| if cache.len() >= MAX_CACHE_SIZE { | ||
| // Remove first entry (simple eviction strategy) | ||
| if let Some(k) = cache.keys().next().cloned() { | ||
| cache.remove(&k); | ||
| } | ||
| } | ||
| cache.insert(key, value); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HashMap iteration order is non-deterministic in Rust - keys().next() can return different entries across runs, making eviction unpredictable. Consider using a proper LRU cache (see src/cortex-file-search/src/cache.rs for an example with access_order tracking) or a simpler FIFO with VecDeque to track insertion order.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cortex-engine/src/config/config_discovery.rs
Line: 28:36
Comment:
HashMap iteration order is non-deterministic in Rust - `keys().next()` can return different entries across runs, making eviction unpredictable. Consider using a proper LRU cache (see `src/cortex-file-search/src/cache.rs` for an example with `access_order` tracking) or a simpler FIFO with `VecDeque` to track insertion order.
How can I resolve this? If you propose a fix, please make it concise.| fn insert_with_eviction<K: Eq + Hash + Clone, V>(cache: &mut HashMap<K, V>, key: K, value: V) { | ||
| if cache.len() >= MAX_CACHE_SIZE { | ||
| // Remove first entry (simple eviction strategy) | ||
| if let Some(k) = cache.keys().next().cloned() { | ||
| cache.remove(&k); | ||
| } | ||
| } | ||
| cache.insert(key, value); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
HashMap iteration order is non-deterministic in Rust - keys().next() returns an arbitrary entry, not necessarily the oldest. For token caching, an LRU policy would be more effective since frequently-used text patterns should remain cached. Consider using a VecDeque or the existing LRU pattern from src/cortex-file-search/src/cache.rs.
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cortex-engine/src/tokenizer.rs
Line: 15:23
Comment:
HashMap iteration order is non-deterministic in Rust - `keys().next()` returns an arbitrary entry, not necessarily the oldest. For token caching, an LRU policy would be more effective since frequently-used text patterns should remain cached. Consider using a `VecDeque` or the existing LRU pattern from `src/cortex-file-search/src/cache.rs`.
How can I resolve this? If you propose a fix, please make it concise.| /// Insert a key-value pair into the cache with eviction when full. | ||
| /// When the cache reaches MAX_CACHE_SIZE, removes an arbitrary entry before inserting. | ||
| fn insert_with_eviction<K: Eq + Hash + Clone, V>(cache: &mut HashMap<K, V>, key: K, value: V) { | ||
| if cache.len() >= MAX_CACHE_SIZE { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edge case: if the cache is exactly at MAX_CACHE_SIZE and we insert a key that already exists (updating its value), we'll unnecessarily evict an entry first. Consider checking if the key exists before eviction:
if cache.len() >= MAX_CACHE_SIZE && !cache.contains_key(&key) {
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cortex-engine/src/config/config_discovery.rs
Line: 29:29
Comment:
Edge case: if the cache is exactly at `MAX_CACHE_SIZE` and we insert a key that already exists (updating its value), we'll unnecessarily evict an entry first. Consider checking if the key exists before eviction:
```
if cache.len() >= MAX_CACHE_SIZE && !cache.contains_key(&key) {
```
How can I resolve this? If you propose a fix, please make it concise.| /// Insert a key-value pair into the cache with eviction when full. | ||
| /// When the cache reaches MAX_CACHE_SIZE, removes an arbitrary entry before inserting. | ||
| fn insert_with_eviction<K: Eq + Hash + Clone, V>(cache: &mut HashMap<K, V>, key: K, value: V) { | ||
| if cache.len() >= MAX_CACHE_SIZE { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edge case: if the cache is exactly at MAX_CACHE_SIZE and we insert a key that already exists (updating its value), we'll unnecessarily evict an entry first. Consider checking if the key exists before eviction:
if cache.len() >= MAX_CACHE_SIZE && !cache.contains_key(&key) {
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/cortex-engine/src/tokenizer.rs
Line: 16:16
Comment:
Edge case: if the cache is exactly at `MAX_CACHE_SIZE` and we insert a key that already exists (updating its value), we'll unnecessarily evict an entry first. Consider checking if the key exists before eviction:
```
if cache.len() >= MAX_CACHE_SIZE && !cache.contains_key(&key) {
```
How can I resolve this? If you propose a fix, please make it concise.This PR consolidates the following memory and storage fixes: - #44: Add cleanup for stale file locks to prevent memory leak - #45: Add cache size limits to prevent unbounded memory growth - #47: Add fsync after file writes to prevent data loss - #50: Bound ToolResponseStore size and cleanup consumed entries - #51: Eliminate TOCTOU races in MCP server and plugin registry - #52: Improve path validation and tilde expansion Key changes: - Added periodic cleanup of stale file locks - Implemented LRU cache limits for config discovery and tokenizer - Added fsync calls after critical file writes - Created bounded ToolResponseStore with automatic cleanup - Fixed time-of-check-time-of-use races - Improved path validation security
|
Consolidated into #80 - fix: consolidated memory and storage improvements |
Summary
Fixes #5146 and #5109 - Unbounded cache growth in config and token caches.
Problem
Global caches grow without limit, potentially causing OOM in long sessions.
Solution
Added MAX_CACHE_SIZE limit with simple eviction when capacity exceeded.