Skip to content

Commit 71ec05e

Browse files
committed
completely remove persistence
1 parent e1c16fe commit 71ec05e

File tree

4 files changed

+6
-212
lines changed

4 files changed

+6
-212
lines changed

.vscode/settings.json

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@
4646
"editor.defaultFormatter": "ms-python.black-formatter"
4747
},
4848
"python.envFile": "${workspaceFolder}/.venv",
49-
"python.languageServer": "Pylance",
49+
"python.languageServer": "None",
5050
"python.testing.pytestEnabled": true,
5151
"python.testing.pytestArgs": [
5252
"${workspaceFolder}/tests",
@@ -55,7 +55,6 @@
5555
"python.testing.unittestEnabled": false,
5656
//"python.envFile": "${workspaceFolder}/python_release.env",
5757

58-
5958
// MYPY
6059
"mypy-type-checker.args": [
6160
"--ignore-missing-imports",

nemoguardrails/cache/interface.py

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -18,10 +18,6 @@
1818
1919
This module defines the abstract base class for cache implementations
2020
that can be used interchangeably throughout the guardrails system.
21-
22-
Cache implementations may optionally support persistence by overriding
23-
the persist_now() method and supports_persistence() method. Persistence
24-
allows cache state to be saved to and loaded from external storage.
2521
"""
2622

2723
from abc import ABC, abstractmethod
@@ -121,31 +117,6 @@ def capacity(self) -> int:
121117
"""
122118
pass
123119

124-
def persist_now(self) -> None:
125-
"""
126-
Force immediate persistence of cache to storage.
127-
128-
This is an optional method that cache implementations can override
129-
if they support persistence. The default implementation does nothing.
130-
131-
Implementations that support persistence should save the current
132-
cache state to their configured storage backend.
133-
"""
134-
# Default no-op implementation
135-
pass
136-
137-
def supports_persistence(self) -> bool:
138-
"""
139-
Check if this cache implementation supports persistence.
140-
141-
Returns:
142-
True if the cache supports persistence, False otherwise.
143-
144-
The default implementation returns False. Cache implementations
145-
that support persistence should override this to return True.
146-
"""
147-
return False
148-
149120
def get_stats(self) -> dict:
150121
"""
151122
Get cache statistics.

nemoguardrails/cache/lfu.py

Lines changed: 0 additions & 156 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,7 @@
1616
"""Least Frequently Used (LFU) cache implementation."""
1717

1818
import asyncio
19-
import json
2019
import logging
21-
import os
2220
import threading
2321
import time
2422
from typing import Any, Callable, Optional
@@ -88,8 +86,6 @@ def __init__(
8886
self,
8987
capacity: int,
9088
track_stats: bool = False,
91-
persistence_interval: Optional[float] = None,
92-
persistence_path: Optional[str] = None,
9389
stats_logging_interval: Optional[float] = None,
9490
) -> None:
9591
"""
@@ -98,8 +94,6 @@ def __init__(
9894
Args:
9995
capacity: Maximum number of items the cache can hold
10096
track_stats: Enable tracking of cache statistics
101-
persistence_interval: Seconds between periodic dumps to disk (None disables persistence)
102-
persistence_path: Path to persistence file (defaults to 'lfu_cache.json' if persistence enabled)
10397
stats_logging_interval: Seconds between periodic stats logging (None disables logging)
10498
"""
10599
if capacity < 0:
@@ -114,12 +108,6 @@ def __init__(
114108
self.freq_map: dict[int, DoublyLinkedList] = {} # frequency -> list of nodes
115109
self.min_freq = 0 # Track minimum frequency for eviction
116110

117-
# Persistence configuration
118-
self.persistence_interval = persistence_interval
119-
self.persistence_path = persistence_path or "lfu_cache.json"
120-
# Initialize to None to ensure first check doesn't trigger immediately
121-
self.last_persist_time = None
122-
123111
# Stats logging configuration
124112
self.stats_logging_interval = stats_logging_interval
125113
# Initialize to None to ensure first check doesn't trigger immediately
@@ -135,10 +123,6 @@ def __init__(
135123
"updates": 0,
136124
}
137125

138-
# Load from disk if persistence is enabled and file exists
139-
if self.persistence_interval is not None:
140-
self._load_from_disk()
141-
142126
def _update_node_freq(self, node: LFUNode) -> None:
143127
"""Update the frequency of a node and move it to the appropriate frequency list."""
144128
old_freq = node.freq
@@ -175,9 +159,6 @@ def get(self, key: Any, default: Any = None) -> Any:
175159
The value associated with the key, or default if not found
176160
"""
177161
with self._lock:
178-
# Check if we should persist
179-
self._check_and_persist()
180-
181162
# Check if we should log stats
182163
self._check_and_log_stats()
183164

@@ -203,9 +184,6 @@ def put(self, key: Any, value: Any) -> None:
203184
value: The value to associate with the key
204185
"""
205186
with self._lock:
206-
# Check if we should persist
207-
self._check_and_persist()
208-
209187
# Check if we should log stats
210188
self._check_and_log_stats()
211189

@@ -312,109 +290,6 @@ def reset_stats(self) -> None:
312290
"updates": 0,
313291
}
314292

315-
def _check_and_persist(self) -> None:
316-
"""Check if enough time has passed and persist to disk if needed."""
317-
if self.persistence_interval is None:
318-
return
319-
320-
current_time = time.time()
321-
322-
# Initialize timestamp on first check
323-
if self.last_persist_time is None:
324-
self.last_persist_time = current_time
325-
return
326-
327-
if current_time - self.last_persist_time >= self.persistence_interval:
328-
self._persist_to_disk()
329-
self.last_persist_time = current_time
330-
331-
def _persist_to_disk(self) -> None:
332-
"""
333-
Serialize cache to disk.
334-
335-
Stores cache data as JSON with node information including keys, values,
336-
frequencies, and timestamps for reconstruction.
337-
"""
338-
if not self.key_map:
339-
# If cache is empty, remove the persistence file
340-
if os.path.exists(self.persistence_path):
341-
os.remove(self.persistence_path)
342-
return
343-
344-
cache_data = {
345-
"capacity": self._capacity,
346-
"min_freq": self.min_freq,
347-
"nodes": [],
348-
}
349-
350-
# Serialize all nodes
351-
for key, node in self.key_map.items():
352-
cache_data["nodes"].append(
353-
{
354-
"key": key,
355-
"value": node.value,
356-
"freq": node.freq,
357-
"created_at": node.created_at,
358-
"accessed_at": node.accessed_at,
359-
}
360-
)
361-
362-
# Write to disk
363-
try:
364-
with open(self.persistence_path, "w") as f:
365-
json.dump(cache_data, f, indent=2)
366-
except Exception as e:
367-
# Silently fail on persistence errors to not disrupt cache operations
368-
pass
369-
370-
def _load_from_disk(self) -> None:
371-
"""
372-
Load cache from disk if persistence file exists.
373-
374-
Reconstructs the cache state including frequency lists and node relationships.
375-
"""
376-
if not os.path.exists(self.persistence_path):
377-
return
378-
379-
try:
380-
with open(self.persistence_path, "r") as f:
381-
cache_data = json.load(f)
382-
383-
# Reconstruct cache
384-
self.min_freq = cache_data.get("min_freq", 0)
385-
386-
for node_data in cache_data.get("nodes", []):
387-
# Create node
388-
node = LFUNode(node_data["key"], node_data["value"])
389-
node.freq = node_data["freq"]
390-
node.created_at = node_data["created_at"]
391-
node.accessed_at = node_data["accessed_at"]
392-
393-
# Add to key map
394-
self.key_map[node.key] = node
395-
396-
# Add to appropriate frequency list
397-
if node.freq not in self.freq_map:
398-
self.freq_map[node.freq] = DoublyLinkedList()
399-
self.freq_map[node.freq].append(node)
400-
401-
except Exception as e:
402-
# If loading fails, start with empty cache
403-
self.key_map.clear()
404-
self.freq_map.clear()
405-
self.min_freq = 0
406-
407-
def persist_now(self) -> None:
408-
"""Force immediate persistence to disk (useful for shutdown)."""
409-
with self._lock:
410-
if self.persistence_interval is not None:
411-
self._persist_to_disk()
412-
self.last_persist_time = time.time()
413-
414-
def supports_persistence(self) -> bool:
415-
"""Check if this cache instance supports persistence."""
416-
return self.persistence_interval is not None
417-
418293
def _check_and_log_stats(self) -> None:
419294
"""Check if enough time has passed and log stats if needed."""
420295
if not self.track_stats or self.stats_logging_interval is None:
@@ -644,34 +519,3 @@ def capacity(self) -> int:
644519
# Reset statistics
645520
stats_cache.reset_stats()
646521
print(f"\nAfter reset: {stats_cache.get_stats()}")
647-
648-
print("\n=== Cache with Persistence ===")
649-
650-
# Create cache with persistence (5 second interval)
651-
persist_cache = LFUCache(
652-
capacity=3, persistence_interval=5.0, persistence_path="test_cache.json"
653-
)
654-
655-
# Add some items
656-
persist_cache.put("item1", "value1")
657-
persist_cache.put("item2", "value2")
658-
persist_cache.put("item3", "value3")
659-
660-
# Force immediate persistence
661-
persist_cache.persist_now()
662-
print("Cache persisted to disk")
663-
664-
# Create new cache instance that will load from disk
665-
new_cache = LFUCache(
666-
capacity=3, persistence_interval=5.0, persistence_path="test_cache.json"
667-
)
668-
669-
# Verify data was loaded
670-
print(f"Loaded item1: {new_cache.get('item1')}") # Should return 'value1'
671-
print(f"Loaded item2: {new_cache.get('item2')}") # Should return 'value2'
672-
print(f"Cache size after loading: {new_cache.size()}") # Should return 3
673-
674-
# Clean up
675-
if os.path.exists("test_cache.json"):
676-
os.remove("test_cache.json")
677-
print("Cleaned up test persistence file")

tests/test_cache_lfu.py

Lines changed: 5 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -17,13 +17,11 @@
1717
Comprehensive test suite for LFU Cache implementation.
1818
1919
Tests all functionality including basic operations, eviction policies,
20-
capacity management, edge cases, and persistence functionality.
20+
capacity management, and edge cases.
2121
"""
2222

2323
import asyncio
24-
import json
2524
import os
26-
import tempfile
2725
import threading
2826
import time
2927
import unittest
@@ -368,15 +366,6 @@ def test_interface_methods_exist(self):
368366
class TestLFUCacheStatsLogging(unittest.TestCase):
369367
"""Test cases for LFU Cache statistics logging functionality."""
370368

371-
def setUp(self):
372-
"""Set up test fixtures."""
373-
self.test_file = tempfile.mktemp()
374-
375-
def tearDown(self):
376-
"""Clean up test files."""
377-
if os.path.exists(self.test_file):
378-
os.remove(self.test_file)
379-
380369
def test_stats_logging_disabled_by_default(self):
381370
"""Test that stats logging is disabled when not configured."""
382371
cache = LFUCache(5, track_stats=True)
@@ -644,15 +633,6 @@ def test_stats_log_format_percentages(self):
644633
class TestContentSafetyCacheStatsConfig(unittest.TestCase):
645634
"""Test cache stats configuration in content safety context."""
646635

647-
def setUp(self):
648-
"""Set up test fixtures."""
649-
self.test_file = tempfile.mktemp()
650-
651-
def tearDown(self):
652-
"""Clean up test files."""
653-
if os.path.exists(self.test_file):
654-
os.remove(self.test_file)
655-
656636
def test_cache_config_with_stats_disabled(self):
657637
"""Test cache configuration with stats disabled."""
658638
from nemoguardrails.rails.llm.config import CacheStatsConfig, ModelCacheConfig
@@ -1193,11 +1173,11 @@ def worker(thread_id):
11931173
f"Wrong value for {key}: expected {value}, got {retrieved}"
11941174
)
11951175

1196-
# Also work with some persistent keys (access multiple times)
1197-
persistent_key = f"persistent_{thread_id % 5}"
1176+
# Also work with some high-frequency keys (access multiple times)
1177+
high_freq_key = f"high_freq_{thread_id % 5}"
11981178
for _ in range(3): # Access 3 times to increase frequency
1199-
small_cache.put(persistent_key, f"persistent_value_{thread_id}")
1200-
small_cache.get(persistent_key)
1179+
small_cache.put(high_freq_key, f"high_freq_value_{thread_id}")
1180+
small_cache.get(high_freq_key)
12011181

12021182
# Run workers
12031183
with ThreadPoolExecutor(max_workers=10) as executor:

0 commit comments

Comments
 (0)