87 lines
2.7 KiB
Python
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()
|