Files

87 lines
2.7 KiB
Python

import time
import json
import logging
logger = logging.getLogger(__name__)
class CacheService:
"""
Servicio de caché híbrido para sesiones y lecturas rápidas de Moodle.
Soporta Redis si está disponible en la infraestructura y hace fallback transparente
a caché en memoria con TTL para entornos de desarrollo y pruebas.
"""
_memory_cache = {}
_redis_client = None
_redis_checked = False
@classmethod
def _get_redis(cls):
if not cls._redis_checked:
cls._redis_checked = True
try:
import redis
from app.models.setting import SystemSetting
redis_url = SystemSetting.get_value('redis_url', 'redis://127.0.0.1:6379/0')
client = redis.from_url(redis_url, socket_connect_timeout=2)
client.ping()
cls._redis_client = client
logger.info("Conexión exitosa a Redis en %s", redis_url)
except Exception as e:
cls._redis_client = None
logger.info("Redis no disponible (%s). Operando con memoria local/TTL.", str(e).split('\n')[0])
return cls._redis_client
@classmethod
def get(cls, key: str, default=None):
r = cls._get_redis()
if r:
try:
val = r.get(key)
if val is not None:
return json.loads(val.decode('utf-8'))
except Exception as e:
logger.debug("Error leyendo de Redis: %s", e)
# Fallback memoria
item = cls._memory_cache.get(key)
if item:
val, expire_at = item
if expire_at is None or expire_at > time.time():
return val
else:
del cls._memory_cache[key]
return default
@classmethod
def set(cls, key: str, value, ttl_seconds: int = 300):
r = cls._get_redis()
if r:
try:
serialized = json.dumps(value)
r.setex(key, ttl_seconds, serialized)
return True
except Exception as e:
logger.debug("Error escribiendo en Redis: %s", e)
expire_at = (time.time() + ttl_seconds) if ttl_seconds else None
cls._memory_cache[key] = (value, expire_at)
return True
@classmethod
def delete(cls, key: str):
r = cls._get_redis()
if r:
try:
r.delete(key)
except Exception:
pass
cls._memory_cache.pop(key, None)
@classmethod
def clear(cls):
cls._memory_cache.clear()
cls._redis_checked = False
cls._redis_client = None
cache_service = CacheService()