quantumtaskai-caprover/core/cache_utils.py
thecyberlearn b0e8c917ef Initial clean CapRover deployment - no secrets
Complete Django AI agent marketplace with security optimizations
and clean deployment documentation without any API keys or secrets.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-09-04 10:50:55 +05:30

165 lines
5.9 KiB
Python

"""
Cache utilities for performance optimization.
"""
from django.core.cache import cache
from django.conf import settings
from functools import wraps
import hashlib
import json
import logging
logger = logging.getLogger(__name__)
def cache_user_data(cache_key_prefix, timeout=None):
"""
Decorator for caching user-specific data.
Args:
cache_key_prefix (str): Prefix for the cache key
timeout (int): Cache timeout in seconds (None for default)
"""
def decorator(func):
@wraps(func)
def wrapper(request, *args, **kwargs):
if not hasattr(request, 'user') or not request.user.is_authenticated:
# Don't cache for anonymous users
return func(request, *args, **kwargs)
# Create unique cache key
cache_key = f"{cache_key_prefix}_{request.user.id}"
if args or kwargs:
# Include args and kwargs in cache key for uniqueness
key_data = f"{args}_{kwargs}"
key_hash = hashlib.md5(key_data.encode()).hexdigest()[:8]
cache_key += f"_{key_hash}"
try:
# Try to get from cache
cached_result = cache.get(cache_key)
if cached_result is not None:
logger.debug(f"Cache hit for {cache_key}")
return cached_result
except Exception as e:
logger.warning(f"Cache get failed for {cache_key}: {e}")
# Execute function and cache result
result = func(request, *args, **kwargs)
try:
# Determine cache timeout
if timeout is None:
cache_timeout = 300 if settings.DEBUG else 1800 # 5 min / 30 min
else:
cache_timeout = timeout
cache.set(cache_key, result, cache_timeout)
logger.debug(f"Cached result for {cache_key} (timeout: {cache_timeout}s)")
except Exception as e:
logger.warning(f"Cache set failed for {cache_key}: {e}")
return result
return wrapper
return decorator
def cache_expensive_query(cache_key, timeout=None):
"""
Decorator for caching expensive database queries.
Args:
cache_key (str): Cache key for the query
timeout (int): Cache timeout in seconds (None for default)
"""
def decorator(func):
@wraps(func)
def wrapper(*args, **kwargs):
# Create unique cache key with function args
full_cache_key = cache_key
if args or kwargs:
key_data = f"{args}_{kwargs}"
key_hash = hashlib.md5(key_data.encode()).hexdigest()[:8]
full_cache_key += f"_{key_hash}"
try:
# Try to get from cache
cached_result = cache.get(full_cache_key)
if cached_result is not None:
logger.debug(f"Query cache hit for {full_cache_key}")
return cached_result
except Exception as e:
logger.warning(f"Query cache get failed for {full_cache_key}: {e}")
# Execute function and cache result
result = func(*args, **kwargs)
try:
# Determine cache timeout
if timeout is None:
cache_timeout = 600 if settings.DEBUG else 3600 # 10 min / 1 hour
else:
cache_timeout = timeout
cache.set(full_cache_key, result, cache_timeout)
logger.debug(f"Cached query result for {full_cache_key} (timeout: {cache_timeout}s)")
except Exception as e:
logger.warning(f"Query cache set failed for {full_cache_key}: {e}")
return result
return wrapper
return decorator
def invalidate_user_cache(user_id, cache_key_prefix):
"""
Invalidate all cache entries for a specific user and prefix.
Args:
user_id (int): User ID
cache_key_prefix (str): Cache key prefix to invalidate
"""
try:
# Create pattern for user-specific cache keys
cache_pattern = f"{cache_key_prefix}_{user_id}"
# Note: This is a simplified implementation
# In production, you might want to use Redis pattern matching
# or maintain a list of cache keys to invalidate
# For now, we'll invalidate common variations
cache_keys_to_invalidate = [
f"{cache_pattern}",
f"{cache_pattern}_*", # This won't work with default cache, needs Redis
]
for key in cache_keys_to_invalidate:
cache.delete(key)
logger.info(f"Invalidated cache for user {user_id} with prefix {cache_key_prefix}")
except Exception as e:
logger.warning(f"Cache invalidation failed for user {user_id}: {e}")
def get_cache_stats():
"""
Get cache statistics (Redis only).
Returns dict with cache statistics or None if not available.
"""
try:
# This only works with Redis backend
if hasattr(cache, '_cache') and hasattr(cache._cache, 'get_client'):
redis_client = cache._cache.get_client()
info = redis_client.info('memory')
return {
'used_memory': info.get('used_memory', 0),
'used_memory_human': info.get('used_memory_human', '0B'),
'used_memory_peak': info.get('used_memory_peak', 0),
'used_memory_peak_human': info.get('used_memory_peak_human', '0B'),
'keyspace_hits': info.get('keyspace_hits', 0),
'keyspace_misses': info.get('keyspace_misses', 0),
}
except Exception as e:
logger.debug(f"Could not get cache stats: {e}")
return None