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
|
from django.core.cache import cache
from django_redis import get_redis_connection
from functools import wraps
import hashlib
import logging
logger = logging.getLogger(__name__)
def generate_cache_key(*args, prefix="", **kwargs):
"""Generate a unique cache key based on args and kwargs"""
# Sort kwargs to ensure consistent key generation
sorted_kwargs = sorted(kwargs.items())
# Convert all arguments to strings and join them
key_parts = [str(arg) for arg in args] + [f"{k}:{v}" for k, v in sorted_kwargs]
key_string = ":".join(key_parts)
# Create an MD5 hash of the key string to ensure safe key length
hash_object = hashlib.md5(key_string.encode())
hashed_key = hash_object.hexdigest()
return f"{prefix}:{hashed_key}" if prefix else hashed_key
def safe_cache_get(key):
"""Safely get data from cache with error handling"""
try:
return cache.get(key)
except Exception as e:
logger.error(f"Cache get error for key {key}: {str(e)}")
return None
def safe_cache_set(key, value, timeout=None):
"""Safely set data in cache with error handling"""
try:
cache.set(key, value, timeout)
return True
except Exception as e:
logger.error(f"Cache set error for key {key}: {str(e)}")
return False
def cache_data(prefix, timeout=60 * 15):
"""
Generic cache decorator that can be used for any function.
Args:
prefix (str): Prefix for the cache key (e.g., 'anime_data', 'streaming_data')
timeout (int): Cache timeout in seconds
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Generate cache key using all arguments
cache_key = generate_cache_key(*args, prefix=prefix, **kwargs)
# Try to get from cache
cached_data = safe_cache_get(cache_key)
if cached_data is not None:
return cached_data
try:
# Get fresh data
result = func(*args, **kwargs)
# Cache the result
safe_cache_set(cache_key, result, timeout)
return result
except Exception as e:
logger.error(
f"Error in {prefix} for args {args}, kwargs {kwargs}: {str(e)}"
)
# On error, return fresh data without caching
return func(*args, **kwargs)
return wrapper
return decorator
def clear_cache(pattern=None, prefix=None):
"""
Clear cache entries based on a pattern or prefix.
Args:
pattern (str): Direct Redis key pattern to match (e.g., '*anime_id*')
prefix (str): Cache prefix to clear (e.g., 'anime_data')
"""
try:
redis_conn = get_redis_connection("default")
if not pattern and not prefix:
# Clear all known prefixes if neither pattern nor prefix specified
prefixes = ["anime_data:*", "streaming_data:*", "search_results:*"]
total_cleared = 0
for p in prefixes:
keys = redis_conn.keys(p)
if keys:
redis_conn.delete(*keys)
total_cleared += len(keys)
logger.info(f"Cleared {len(keys)} entries for pattern {p}")
return total_cleared
if prefix:
pattern = f"{prefix}:*"
keys = redis_conn.keys(pattern)
if keys:
redis_conn.delete(*keys)
logger.info(f"Cleared {len(keys)} cache entries matching {pattern}")
return len(keys)
return 0
except Exception as e:
logger.error(f"Error clearing cache with pattern {pattern}: {str(e)}")
return 0
|