feat: MTG database integration with Redis caching
- Added MTG card ORM models (mtg_cards, mtg_sets tables) - Created card_database service with search, get_by_name, get_by_set - Added Redis client with caching layer (3600s TTL default) - Created card router with caching on all endpoints: - Search cards (5min cache) - Get card by name (10min cache) - Get cards by set (15min cache) - Get card types/rarities (30min cache) - Get sets (1hr cache) - Get statistics (1hr cache) - Updated settings.py: - Added JWT_SECRET_KEY field - Added DB_CONFIG and REDIS_CONFIG dictionaries - Updated security.py to use JWT_SECRET_KEY with fallback - Updated auth.py to use timezone-aware datetimes - Updated refresh_mtg.py to use settings instead of os.environ - Updated mtg_monitor.py to use settings for connections - Added services package with __init__.py All 20 tests passing.
This commit is contained in:
@@ -0,0 +1,90 @@
|
||||
"""Redis client and caching layer."""
|
||||
import json
|
||||
import logging
|
||||
from typing import Any, Optional
|
||||
|
||||
import redis.asyncio as aioredis
|
||||
from app.core.settings import get_settings
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
settings = get_settings()
|
||||
|
||||
# Redis client instance
|
||||
redis_client: Optional[aioredis.Redis] = None
|
||||
|
||||
|
||||
async def get_redis() -> aioredis.Redis:
|
||||
"""Get Redis client instance."""
|
||||
global redis_client
|
||||
if redis_client is None:
|
||||
try:
|
||||
redis_client = aioredis.from_url(
|
||||
settings.REDIS_URL,
|
||||
decode_responses=True,
|
||||
socket_connect_timeout=5,
|
||||
socket_timeout=5,
|
||||
)
|
||||
await redis_client.ping()
|
||||
logger.info("Connected to Redis")
|
||||
except Exception as e:
|
||||
logger.warning(f"Failed to connect to Redis: {e}")
|
||||
redis_client = None
|
||||
return redis_client
|
||||
|
||||
|
||||
async def close_redis():
|
||||
"""Close Redis connection."""
|
||||
global redis_client
|
||||
if redis_client:
|
||||
await redis_client.close()
|
||||
redis_client = None
|
||||
logger.info("Closed Redis connection")
|
||||
|
||||
|
||||
async def cache_get(key: str) -> Optional[str]:
|
||||
"""Get cached value."""
|
||||
try:
|
||||
client = await get_redis()
|
||||
if not client:
|
||||
return None
|
||||
value = await client.get(key)
|
||||
return value
|
||||
except Exception as e:
|
||||
logger.error(f"Cache get error for key {key}: {e}")
|
||||
return None
|
||||
|
||||
|
||||
async def cache_set(key: str, value: str, ttl: int = 3600):
|
||||
"""Set cached value with TTL (default 1 hour)."""
|
||||
try:
|
||||
client = await get_redis()
|
||||
if not client:
|
||||
return
|
||||
await client.set(key, value, ex=ttl)
|
||||
except Exception as e:
|
||||
logger.error(f"Cache set error for key {key}: {e}")
|
||||
|
||||
|
||||
async def cache_delete(key: str):
|
||||
"""Delete cached value."""
|
||||
try:
|
||||
client = await get_redis()
|
||||
if not client:
|
||||
return
|
||||
await client.delete(key)
|
||||
except Exception as e:
|
||||
logger.error(f"Cache delete error for key {key}: {e}")
|
||||
|
||||
|
||||
async def cache_invalidate_pattern(pattern: str):
|
||||
"""Invalidate all cached keys matching pattern."""
|
||||
try:
|
||||
client = await get_redis()
|
||||
if not client:
|
||||
return
|
||||
keys = await client.keys(pattern)
|
||||
if keys:
|
||||
await client.delete(*keys)
|
||||
except Exception as e:
|
||||
logger.error(f"Cache invalidate error for pattern {pattern}: {e}")
|
||||
Reference in New Issue
Block a user