kv-netflix/backend/cache.py

100 lines
2.8 KiB
Python

"""
Cache module - Redis with in-memory fallback for development
"""
import json
import time
from typing import Optional, Any
from config import settings
from logging_config import get_logger
logger = get_logger("cache")
# Try to import redis, fall back to in-memory if not available
try:
import redis
REDIS_AVAILABLE = True
except ImportError:
REDIS_AVAILABLE = False
class InMemoryCache:
"""Simple in-memory cache with TTL support for development"""
def __init__(self):
self._cache: dict[str, tuple[Any, float]] = {}
def get(self, key: str) -> Optional[str]:
if key in self._cache:
value, expiry = self._cache[key]
if time.time() < expiry:
return value
del self._cache[key]
return None
def set(self, key: str, value: str, ex: int = 10800) -> None:
self._cache[key] = (value, time.time() + ex)
def delete(self, key: str) -> None:
self._cache.pop(key, None)
def exists(self, key: str) -> bool:
return self.get(key) is not None
class CacheManager:
"""
Cache manager with Redis support and in-memory fallback.
Default TTL: 3 hours (10800 seconds)
"""
def __init__(self):
self.client = None
self.is_redis = False
self._connect()
@property
def default_ttl(self) -> int:
"""Get default TTL from settings"""
return settings.cache_default_ttl
def _connect(self):
redis_url = settings.redis_url
if REDIS_AVAILABLE:
try:
self.client = redis.from_url(redis_url, decode_responses=True)
self.client.ping()
self.is_redis = True
logger.info("Connected to Redis")
except Exception as e:
logger.warning(f"Redis not available ({e}), using in-memory cache")
self.client = InMemoryCache()
else:
logger.warning("Redis package not installed, using in-memory cache")
self.client = InMemoryCache()
def get(self, key: str) -> Optional[Any]:
"""Get cached data by key"""
data = self.client.get(key)
if data:
try:
return json.loads(data)
except (json.JSONDecodeError, TypeError):
return data
return None
def set(self, key: str, value: Any, ttl: int = None) -> None:
"""Cache data by key"""
if isinstance(value, (dict, list)):
value = json.dumps(value)
self.client.set(key, value, ex=ttl or self.default_ttl)
def invalidate(self, key: str) -> None:
"""Remove cached data"""
self.client.delete(key)
# Singleton instance
cache = CacheManager()