diff options
| author | Bobby <[email protected]> | 2024-12-24 06:50:59 -0500 |
|---|---|---|
| committer | Bobby <[email protected]> | 2024-12-24 06:50:59 -0500 |
| commit | 6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d (patch) | |
| tree | 273148d564c11ae46e9f96c0231ce57427f5591f /internal/cache_utils.py | |
| parent | 4feba2452a151ed999d52d4a0d53b0b0584bf70e (diff) | |
| download | thatcomputerscientist-6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d.tar.xz thatcomputerscientist-6df9f0dc40501e8f55bcc883dfe5be65e60d3c3d.zip | |
bucket load of things
Diffstat (limited to 'internal/cache_utils.py')
| -rw-r--r-- | internal/cache_utils.py | 118 |
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 |
