Skip to content

Commit 8108cb5

Browse files
committed
feat: add Redis caching to TTS API for performance optimization
- Integrate Railway Redis for permanent TTS response caching - Cache audio files as base64 encoded data with unique keys - Implement graceful fallback when Redis is unavailable - Add cache hit/miss headers for monitoring - Reduce server load and improve response times
1 parent 11f54b0 commit 8108cb5

File tree

4 files changed

+74
-0
lines changed

4 files changed

+74
-0
lines changed

.env_template

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,3 +17,4 @@ COOKIES_MAX_AGE_IN_DAYS='2'
1717
OPENAI_API_KEY=''
1818

1919
API_URL='http://127.0.0.1:8000'
20+
REDIS_URL='redis://localhost:6379'

src/apps/speech/views.py

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
1+
import hashlib
2+
import base64
3+
import os
14
from io import BytesIO
25

6+
import redis
7+
from django.conf import settings
38
from django.http import FileResponse
49
from drf_yasg.utils import swagger_auto_schema
510
from gtts import gTTS
@@ -10,6 +15,20 @@
1015
from apps.api.utils import serializer_to_manual_parameters
1116
from apps.speech.serializers import TextToSpeechSerializer
1217

18+
# Redis connection setup
19+
try:
20+
redis_url = os.getenv('REDIS_URL') or getattr(settings, 'REDIS_URL', None)
21+
if redis_url:
22+
redis_client = redis.from_url(redis_url, decode_responses=False) # Keep as False for binary data
23+
redis_client.ping() # Test connection
24+
REDIS_AVAILABLE = True
25+
else:
26+
redis_client = None
27+
REDIS_AVAILABLE = False
28+
except (redis.ConnectionError, AttributeError):
29+
redis_client = None
30+
REDIS_AVAILABLE = False
31+
1332

1433
class TextToSpeechAPIView(APIView):
1534
@swagger_auto_schema(manual_parameters=serializer_to_manual_parameters(TextToSpeechSerializer))
@@ -23,18 +42,57 @@ def get(self, request, *args, **kwargs):
2342
if not input_text:
2443
return Response({"error": "The text parameter is missing."}, status=status.HTTP_400_BAD_REQUEST)
2544

45+
# Create cache key from text, language, and slow parameter
2646
slow = False if language == "bn" else True
47+
cache_data = f"{input_text}_{language}_{slow}"
48+
cache_key = f"tts:{hashlib.md5(cache_data.encode('utf-8')).hexdigest()}"
49+
50+
# Try to get from Redis cache first
51+
if REDIS_AVAILABLE and redis_client:
52+
try:
53+
cached_audio = redis_client.get(cache_key)
54+
if cached_audio:
55+
# Convert base64 back to bytes
56+
audio_data = base64.b64decode(cached_audio)
57+
output_file = BytesIO(audio_data)
2758

59+
response = FileResponse(output_file, content_type="audio/mpeg")
60+
response["Content-Disposition"] = 'attachment; filename="output.mp3"'
61+
response["X-Cache-Status"] = "HIT"
62+
return response
63+
except redis.RedisError:
64+
# If Redis fails, continue without caching
65+
pass
66+
67+
# Generate TTS if not in cache
2868
try:
2969
tts = gTTS(text=input_text, lang=language, slow=slow)
3070

3171
output_file = BytesIO()
3272
tts.write_to_fp(output_file)
3373
output_file.seek(0)
3474

75+
# Read audio data for caching
76+
audio_data = output_file.read()
77+
output_file.seek(0) # Reset for response
78+
79+
# Cache the audio data in Redis permanently
80+
if REDIS_AVAILABLE and redis_client:
81+
try:
82+
# Encode binary data as base64 for Redis storage
83+
encoded_audio = base64.b64encode(audio_data).decode('utf-8')
84+
85+
# Cache forever - TTS results never change
86+
redis_client.set(cache_key, encoded_audio)
87+
except redis.RedisError:
88+
# If caching fails, continue serving the response
89+
pass
90+
3591
response = FileResponse(output_file, content_type="audio/mpeg")
3692
response["Content-Disposition"] = 'attachment; filename="output.mp3"'
93+
response["X-Cache-Status"] = "MISS"
3794
return response
95+
3896
except Exception as e:
3997
return Response(
4098
{"error": f"Error generating audio: {str(e)}"}, status=status.HTTP_500_INTERNAL_SERVER_ERROR

src/core/settings.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -226,3 +226,17 @@
226226
SITE_ID = 1
227227

228228
OPENAI_API_KEY = config("OPENAI_API_KEY", default="")
229+
230+
# Redis Configuration for Railway TTS Caching
231+
REDIS_URL = config("REDIS_URL", default="redis://localhost:6379")
232+
233+
# Optional: Django Cache Framework with Redis (for other caching needs)
234+
CACHES = {
235+
'default': {
236+
'BACKEND': 'django.core.cache.backends.redis.RedisCache',
237+
'LOCATION': REDIS_URL,
238+
'OPTIONS': {
239+
'CLIENT_CLASS': 'django_redis.client.DefaultClient',
240+
}
241+
}
242+
}

src/requirements.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,4 @@ setuptools>=71.1.0
2626
gTTS>=2.5.1
2727
# googletrans==4.0.0-rc1
2828
openai>=1.41.1
29+
redis>=6.2.0

0 commit comments

Comments
 (0)