sentiment-api / src /cache.py
Syed Arfan
Add Docker Compose setup with PostgreSQL, Redis, and Nginx; implement caching and database models
66da1e3
"""
Redis cache layer for sentiment analysis API
Caches ML inference results to avoid redundant model calls
Uses Redis for sub-millisecond lookup times
"""
import redis
import hashlib
import json
import os
from typing import Optional, Dict, Any
# Get Redis URL from environment variable
REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
# Create Redis client
# decode_responses=True converts bytes to strings automatically
redis_client = redis.from_url(REDIS_URL, decode_responses=True)
# Cache TTL (Time To Live) - how long to keep cached results
CACHE_TTL_SECONDS = 3600 # 1 hour
def generate_cache_key(text: str) -> str:
"""
Generate a unique cache key for the input text
Uses SHA-256 hash to create consistent keys
Same text always generates same key
Args:
text: Input text to analyze
Returns:
Cache key string (e.g., "sentiment:abc123...")
"""
# Create hash of the text (consistent for same input)
text_hash = hashlib.sha256(text.encode()).hexdigest()[:16]
return f"sentiment:{text_hash}"
def get_cached_result(text: str) -> Optional[Dict[str, Any]]:
"""
Retrieve cached sentiment analysis result
Args:
text: Input text to look up
Returns:
Cached result dict if found, None if cache miss
"""
try:
cache_key = generate_cache_key(text)
cached_data = redis_client.get(cache_key)
if cached_data:
# Parse JSON string back to dict
return json.loads(cached_data)
return None
except Exception as e:
# If Redis fails, log but don't crash
print(f"Cache retrieval error: {e}")
return None
def cache_result(text: str, result: Dict[str, Any]) -> bool:
"""
Store sentiment analysis result in cache
Args:
text: Input text that was analyzed
result: Analysis result to cache
Returns:
True if cached successfully, False otherwise
"""
try:
cache_key = generate_cache_key(text)
# Convert dict to JSON string
result_json = json.dumps(result)
# Store in Redis with TTL
redis_client.setex(
cache_key,
CACHE_TTL_SECONDS,
result_json
)
return True
except Exception as e:
print(f"Cache storage error: {e}")
return False
def get_cache_stats() -> Dict[str, Any]:
"""
Get Redis cache statistics
Returns:
Dict with cache info (memory usage, keys, hits, etc.)
"""
try:
info = redis_client.info("stats")
memory = redis_client.info("memory")
# Count sentiment-related keys
sentiment_keys = len(redis_client.keys("sentiment:*"))
return {
"status": "connected",
"total_keys": redis_client.dbsize(),
"sentiment_keys": sentiment_keys,
"memory_used_mb": round(memory["used_memory"] / 1024 / 1024, 2),
"hits": info.get("keyspace_hits", 0),
"misses": info.get("keyspace_misses", 0),
"hit_rate": round(
info.get("keyspace_hits", 0) /
max(info.get("keyspace_hits", 0) + info.get("keyspace_misses", 0), 1) * 100,
2
)
}
except Exception as e:
return {
"status": "error",
"error": str(e)
}
def clear_cache() -> bool:
"""
Clear all sentiment cache entries
WARNING: This removes all cached results
Returns:
True if cleared successfully
"""
try:
# Get all sentiment keys
keys = redis_client.keys("sentiment:*")
if keys:
redis_client.delete(*keys)
return True
except Exception as e:
print(f"Cache clear error: {e}")
return False