-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathautomatic_cache_manager.py
More file actions
212 lines (165 loc) Β· 8.09 KB
/
Copy pathautomatic_cache_manager.py
File metadata and controls
212 lines (165 loc) Β· 8.09 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
#!/usr/bin/env python3
"""
Automatic Cache Management System
Prevents stale cache results from causing issues
"""
import asyncio
import time
import logging
from typing import Dict, List, Any
from datetime import datetime, timedelta
logger = logging.getLogger(__name__)
class AutomaticCacheManager:
"""Automatic cache management to prevent stale results"""
def __init__(self, database_search, nlu_engine):
self.database_search = database_search
self.nlu_engine = nlu_engine
self.last_cleanup = time.time()
self.cleanup_interval = 3600 # 1 hour
self.grade_cache_tracker = {} # Track grade-specific cache usage
async def setup_automatic_management(self):
"""Setup automatic cache management"""
logger.info("π§ Setting up automatic cache management")
# Clear stale caches on startup
await self.clear_stale_caches()
# Setup periodic cleanup
asyncio.create_task(self.periodic_cleanup())
logger.info("β
Automatic cache management active")
async def clear_stale_caches(self):
"""Clear all stale cache entries"""
try:
# Clear Redis cache
if hasattr(self.database_search, 'redis') and self.database_search.redis_available:
self.database_search.redis.flushall()
logger.info("ποΈ Cleared all Redis caches")
# Clear in-memory caches
if hasattr(self.database_search, 'language_detector'):
if hasattr(self.database_search.language_detector, 'language_cache'):
self.database_search.language_detector.language_cache.clear()
logger.info("ποΈ Cleared language detection cache")
if hasattr(self.nlu_engine, 'cache'):
self.nlu_engine.cache.clear()
logger.info("ποΈ Cleared NLU cache")
# Reset cache tracking
self.grade_cache_tracker.clear()
except Exception as e:
logger.error(f"Cache cleanup failed: {e}")
async def periodic_cleanup(self):
"""Periodic cache cleanup to prevent memory issues"""
while True:
try:
await asyncio.sleep(self.cleanup_interval)
# Clean up expired entries
await self.cleanup_expired_entries()
# Clean up grade-specific conflicts
await self.cleanup_grade_conflicts()
# Monitor cache health
await self.monitor_cache_health()
self.last_cleanup = time.time()
except Exception as e:
logger.error(f"Periodic cleanup failed: {e}")
async def cleanup_expired_entries(self):
"""Clean up expired cache entries"""
if not hasattr(self.database_search, 'redis') or not self.database_search.redis_available:
return
try:
keys = self.database_search.redis.keys('*')
expired_keys = []
for key in keys:
ttl = self.database_search.redis.ttl(key)
if ttl == -1: # No expiration
expired_keys.append(key)
elif ttl < 300: # Less than 5 minutes
expired_keys.append(key)
if expired_keys:
self.database_search.redis.delete(*expired_keys)
logger.info(f"π§Ή Cleaned up {len(expired_keys)} expired cache entries")
except Exception as e:
logger.error(f"Expired entry cleanup failed: {e}")
async def cleanup_grade_conflicts(self):
"""Clean up grade-specific cache conflicts"""
if not hasattr(self.database_search, 'redis') or not self.database_search.redis_available:
return
try:
keys = self.database_search.redis.keys('*')
conflict_keys = []
for key in keys:
# Check if key contains grade information
if 'grade' in key.lower():
# Check if this key might cause conflicts
ttl = self.database_search.redis.ttl(key)
if ttl > 1800: # More than 30 minutes
conflict_keys.append(key)
if conflict_keys:
self.database_search.redis.delete(*conflict_keys)
logger.info(f"π§Ή Cleaned up {len(conflict_keys)} potentially conflicting cache entries")
except Exception as e:
logger.error(f"Grade conflict cleanup failed: {e}")
async def monitor_cache_health(self):
"""Monitor cache health and performance"""
if not hasattr(self.database_search, 'redis') or not self.database_search.redis_available:
return
try:
keys = self.database_search.redis.keys('*')
total_keys = len(keys)
# Check for grade-specific cache distribution
grade_keys = {}
for key in keys:
for grade in ['1', '2', '3', '4', '5', '6']:
if f'grade {grade}' in key.lower():
grade_keys[grade] = grade_keys.get(grade, 0) + 1
# Log cache health
logger.info(f"π Cache Health: {total_keys} total keys")
for grade, count in grade_keys.items():
logger.info(f" Grade {grade}: {count} cache entries")
# Alert if cache is getting too large
if total_keys > 1000:
logger.warning(f"β οΈ Cache size large: {total_keys} keys - consider cleanup")
except Exception as e:
logger.error(f"Cache health monitoring failed: {e}")
async def invalidate_grade_cache(self, grade_num: str):
"""Invalidate all cache entries for a specific grade"""
if not hasattr(self.database_search, 'redis') or not self.database_search.redis_available:
return
try:
keys = self.database_search.redis.keys('*')
grade_keys = [key for key in keys if f'grade {grade_num}' in key.lower()]
if grade_keys:
self.database_search.redis.delete(*grade_keys)
logger.info(f"ποΈ Invalidated {len(grade_keys)} cache entries for Grade {grade_num}")
except Exception as e:
logger.error(f"Grade cache invalidation failed: {e}")
async def force_cache_refresh(self):
"""Force refresh all caches"""
logger.info("π Forcing complete cache refresh")
await self.clear_stale_caches()
logger.info("β
Cache refresh complete")
# Integration with chatbot
async def setup_automatic_cache_management(chatbot):
"""Setup automatic cache management for the chatbot"""
try:
cache_manager = AutomaticCacheManager(
chatbot.database_search,
chatbot.nlu_engine
)
await cache_manager.setup_automatic_management()
# Store reference for manual operations
chatbot.cache_manager = cache_manager
logger.info("β
Automatic cache management integrated")
return cache_manager
except Exception as e:
logger.error(f"Cache management setup failed: {e}")
return None
if __name__ == "__main__":
# Test the cache management system
async def test_cache_management():
print("π§ͺ Testing Automatic Cache Management")
# This would be integrated into the chatbot initialization
print("β
Cache management system ready")
print("π§ Features:")
print(" - Automatic cache cleanup every hour")
print(" - Grade-specific cache invalidation")
print(" - Stale entry detection and removal")
print(" - Cache health monitoring")
print(" - Conflict prevention")
asyncio.run(test_cache_management())