174 lines
5.1 KiB
Python
174 lines
5.1 KiB
Python
"""
|
|
Redis 캐싱 유틸리티
|
|
|
|
Dashboard API 성능 최적화를 위한 Redis 캐싱 기능을 제공합니다.
|
|
YouTube Analytics API 호출 결과를 캐싱하여 중복 요청을 방지합니다.
|
|
"""
|
|
|
|
from typing import Optional
|
|
|
|
from redis.asyncio import Redis
|
|
|
|
from app.utils.logger import get_logger
|
|
from config import db_settings
|
|
|
|
logger = get_logger("redis_cache")
|
|
|
|
# Dashboard 전용 Redis 클라이언트 (db=3 사용)
|
|
_cache_client = Redis(
|
|
host=db_settings.REDIS_HOST,
|
|
port=db_settings.REDIS_PORT,
|
|
db=3,
|
|
decode_responses=True,
|
|
)
|
|
|
|
|
|
async def get_cache(key: str) -> Optional[str]:
|
|
"""
|
|
Redis 캐시에서 값을 조회합니다.
|
|
|
|
Args:
|
|
key: 캐시 키
|
|
|
|
Returns:
|
|
캐시된 값 (문자열) 또는 None (캐시 미스)
|
|
|
|
Example:
|
|
>>> cached_data = await get_cache("dashboard:user123:2026-01-01:2026-12-31")
|
|
>>> if cached_data:
|
|
>>> return json.loads(cached_data)
|
|
"""
|
|
try:
|
|
logger.debug(f"[GET_CACHE] 캐시 조회 시작 - key: {key}")
|
|
value = await _cache_client.get(key)
|
|
|
|
if value:
|
|
logger.debug(f"[GET_CACHE] 캐시 HIT - key: {key}")
|
|
else:
|
|
logger.debug(f"[GET_CACHE] 캐시 MISS - key: {key}")
|
|
|
|
return value
|
|
except Exception as e:
|
|
logger.error(f"[GET_CACHE] 캐시 조회 실패 - key: {key}, error: {e}")
|
|
return None # 캐시 실패 시 None 반환 (원본 데이터 조회하도록 유도)
|
|
|
|
|
|
async def set_cache(key: str, value: str, ttl: int = 43200) -> bool:
|
|
"""
|
|
Redis 캐시에 값을 저장합니다.
|
|
|
|
Args:
|
|
key: 캐시 키
|
|
value: 저장할 값 (문자열)
|
|
ttl: 캐시 만료 시간 (초). 기본값: 43200초 (12시간)
|
|
|
|
Returns:
|
|
성공 여부
|
|
|
|
Example:
|
|
>>> import json
|
|
>>> data = {"views": 1000, "likes": 50}
|
|
>>> await set_cache("dashboard:user123:2026-01-01:2026-12-31", json.dumps(data), ttl=3600)
|
|
"""
|
|
try:
|
|
logger.debug(f"[SET_CACHE] 캐시 저장 시작 - key: {key}, ttl: {ttl}s")
|
|
await _cache_client.setex(key, ttl, value)
|
|
logger.debug(f"[SET_CACHE] 캐시 저장 성공 - key: {key}")
|
|
return True
|
|
except Exception as e:
|
|
logger.error(f"[SET_CACHE] 캐시 저장 실패 - key: {key}, error: {e}")
|
|
return False
|
|
|
|
|
|
async def delete_cache(key: str) -> bool:
|
|
"""
|
|
Redis 캐시에서 값을 삭제합니다.
|
|
|
|
Args:
|
|
key: 삭제할 캐시 키
|
|
|
|
Returns:
|
|
성공 여부
|
|
|
|
Example:
|
|
>>> await delete_cache("dashboard:user123:2026-01-01:2026-12-31")
|
|
"""
|
|
try:
|
|
logger.debug(f"[DELETE_CACHE] 캐시 삭제 시작 - key: {key}")
|
|
deleted_count = await _cache_client.delete(key)
|
|
logger.debug(
|
|
f"[DELETE_CACHE] 캐시 삭제 완료 - key: {key}, deleted: {deleted_count}"
|
|
)
|
|
return deleted_count > 0
|
|
except Exception as e:
|
|
logger.error(f"[DELETE_CACHE] 캐시 삭제 실패 - key: {key}, error: {e}")
|
|
return False
|
|
|
|
|
|
async def delete_cache_pattern(pattern: str) -> int:
|
|
"""
|
|
패턴에 매칭되는 모든 캐시 키를 삭제합니다.
|
|
|
|
Args:
|
|
pattern: 삭제할 키 패턴 (예: "dashboard:user123:*")
|
|
|
|
Returns:
|
|
삭제된 키 개수
|
|
|
|
Example:
|
|
>>> # 특정 사용자의 모든 대시보드 캐시 삭제
|
|
>>> deleted = await delete_cache_pattern("dashboard:user123:*")
|
|
>>> print(f"{deleted}개의 캐시 삭제됨")
|
|
|
|
Note:
|
|
대량의 키 삭제 시 성능에 영향을 줄 수 있으므로 주의해서 사용하세요.
|
|
"""
|
|
try:
|
|
logger.debug(f"[DELETE_CACHE_PATTERN] 패턴 캐시 삭제 시작 - pattern: {pattern}")
|
|
|
|
# 패턴에 매칭되는 모든 키 조회
|
|
keys = []
|
|
async for key in _cache_client.scan_iter(match=pattern):
|
|
keys.append(key)
|
|
|
|
if not keys:
|
|
logger.debug(f"[DELETE_CACHE_PATTERN] 삭제할 키 없음 - pattern: {pattern}")
|
|
return 0
|
|
|
|
# 모든 키 삭제
|
|
deleted_count = await _cache_client.delete(*keys)
|
|
logger.debug(
|
|
f"[DELETE_CACHE_PATTERN] 패턴 캐시 삭제 완료 - "
|
|
f"pattern: {pattern}, deleted: {deleted_count}"
|
|
)
|
|
return deleted_count
|
|
|
|
except Exception as e:
|
|
logger.error(
|
|
f"[DELETE_CACHE_PATTERN] 패턴 캐시 삭제 실패 - pattern: {pattern}, error: {e}"
|
|
)
|
|
return 0
|
|
|
|
|
|
async def close_cache_client():
|
|
"""
|
|
Redis 클라이언트 연결을 종료합니다.
|
|
|
|
애플리케이션 종료 시 호출되어야 합니다.
|
|
main.py의 shutdown 이벤트 핸들러에서 사용하세요.
|
|
|
|
Example:
|
|
>>> # main.py
|
|
>>> @app.on_event("shutdown")
|
|
>>> async def shutdown_event():
|
|
>>> await close_cache_client()
|
|
"""
|
|
try:
|
|
logger.info("[CLOSE_CACHE_CLIENT] Redis 캐시 클라이언트 종료 중...")
|
|
await _cache_client.close()
|
|
logger.info("[CLOSE_CACHE_CLIENT] Redis 캐시 클라이언트 종료 완료")
|
|
except Exception as e:
|
|
logger.error(
|
|
f"[CLOSE_CACHE_CLIENT] Redis 캐시 클라이언트 종료 실패 - error: {e}"
|
|
)
|