Spaces:
Sleeping
Sleeping
| import redis.asyncio as redis | |
| import json | |
| from typing import Optional, Any | |
| import sys | |
| import os | |
| # Add project root to sys.path to allow importing hot_cache | |
| sys.path.insert(0, os.path.abspath(os.path.join(os.path.dirname(__file__), '../../..'))) | |
| from backend.app.config import settings | |
| from hot_cache import LRUCache | |
| class CacheService: | |
| """ | |
| Cache service class using Redis with a fallback to in-memory cache. | |
| """ | |
| _client: Optional[redis.Redis] = None | |
| _redis_unavailable: bool = False | |
| _memory_cache: LRUCache = LRUCache(max_size=100) | |
| async def _get_redis_client(self) -> Optional[redis.Redis]: | |
| """Initializes and returns a Redis client if available.""" | |
| if self._redis_unavailable: | |
| return None | |
| if self._client is None: | |
| try: | |
| print("--- Initializing Redis Client ---") | |
| # Set a timeout to avoid long waits if Redis is not running | |
| client = redis.from_url( | |
| settings.REDIS_URL, socket_connect_timeout=1, encoding="utf-8", decode_responses=True | |
| ) | |
| await client.ping() | |
| self._client = client | |
| print("--- Redis Client Initialized Successfully. Redis caching is active. ---") | |
| except Exception as e: | |
| print(f"--- Redis connection failed: {e}. Falling back to in-memory cache. ---") | |
| self._redis_unavailable = True | |
| self._client = None | |
| return self._client | |
| async def get(self, key: str) -> Optional[Any]: | |
| """Fetches a value from the cache by key.""" | |
| client = await self._get_redis_client() | |
| if client: | |
| try: | |
| cached_value = await client.get(key) | |
| if cached_value: | |
| print(f"REDIS CACHE HIT for key: {key}") | |
| return json.loads(cached_value) | |
| except Exception as e: | |
| print(f"Redis GET error: {e}. Disabling Redis for this session.") | |
| self.__class__._redis_unavailable = True # Use class attribute to disable for all instances | |
| self.__class__._client = None | |
| # Fallback to in-memory cache | |
| value = self._memory_cache.get(key) | |
| if value: | |
| print(f"MEMORY CACHE HIT for key: {key}") | |
| return value | |
| print(f"CACHE MISS for key: {key}") | |
| return None | |
| async def set(self, key: str, value: Any, ttl: int = 3600): | |
| """Sets a key-value pair in the cache with a TTL.""" | |
| client = await self._get_redis_client() | |
| if client: | |
| try: | |
| value_to_cache = json.dumps(value, default=str) | |
| await client.set(key, value_to_cache, ex=ttl) | |
| print(f"REDIS CACHE SET for key: {key}") | |
| return | |
| except Exception as e: | |
| print(f"Redis SET error: {e}. Disabling Redis for this session.") | |
| self.__class__._redis_unavailable = True # Use class attribute to disable for all instances | |
| self.__class__._client = None | |
| # Fallback to in-memory cache | |
| self._memory_cache[key] = value | |
| print(f"MEMORY CACHE SET for key: {key}") | |
| # --- Dependency Injection Function --- | |
| async def get_cache_service() -> "CacheService": | |
| """ | |
| Factory function for dependency injection. | |
| Returns an instance of the CacheService. | |
| """ | |
| return CacheService() | |