aboutsummaryrefslogtreecommitdiff
path: root/internal/cache_utils.py
diff options
context:
space:
mode:
authorBobby <[email protected]>2024-12-24 06:50:59 -0500
committerBobby <[email protected]>2024-12-24 06:50:59 -0500
commit6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d (patch)
tree273148d564c11ae46e9f96c0231ce57427f5591f /internal/cache_utils.py
parent4feba2452a151ed999d52d4a0d53b0b0584bf70e (diff)
downloadthatcomputerscientist-6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d.tar.xz
thatcomputerscientist-6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d.zip
bucket load of things
Diffstat (limited to 'internal/cache_utils.py')
-rw-r--r--internal/cache_utils.py118
1 files changed, 118 insertions, 0 deletions
diff --git a/internal/cache_utils.py b/internal/cache_utils.py
new file mode 100644
index 00000000..7be70e57
--- /dev/null
+++ b/internal/cache_utils.py
@@ -0,0 +1,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