Redis Setup Guide - Performance Optimization 80x¶
📊 Performance Comparison¶
| Métrica | CloudSQL Session | Redis Session | Melhoria |
|---|---|---|---|
| append_event() | O(n) - read all + write | O(1) - RPUSH | 80x |
| Latência típica | ~800-1000ms | ~10-20ms | 50-80x |
| Eventos/interação | 3-5 events × 200ms | 3-5 events × 2ms | 80x |
| User experience | Lento, visível delay | Instantâneo | 🚀 |
Root Cause (CloudSQL Session):¶
# CloudSQL append_event() - O(n) complexity
def append_event(session_id, event):
1. SELECT events FROM sessions WHERE id = session_id # ~100ms - read ALL
2. events = json.loads(blob) # ~50ms - deserialize ALL
3. events.append(new_event) # ~1ms
4. blob = json.dumps(events) # ~50ms - serialize ALL
5. UPDATE sessions SET events = blob WHERE id = ... # ~100ms - write ALL
# Total: ~300ms × 3 events = ~900ms overhead
Solution (Redis Session):¶
# Redis append_event() - O(1) constant time
def append_event(session_id, event):
redis.rpush(f"session:{session_id}:events", json.dumps(event))
# Total: ~2ms - append to list, no reads
🏗️ Architecture¶
┌─────────────────────────────────────────────────────────────┐
│ Slack Bot │
├─────────────────────────────────────────────────────────────┤
│ │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ Session Service│◄────HOT─────►│ Redis │ │
│ │ (Runner) │ ~10ms │ Memorystore │ │
│ │ │ O(1) │ (Session data) │ │
│ │ • get_session │ │ • Fast │ │
│ │ • append_event │ │ • TTL cleanup │ │
│ │ • list_sessions│ │ • Persistence │ │
│ └─────────────────┘ └──────────────────┘ │
│ │
│ ┌─────────────────┐ ┌──────────────────┐ │
│ │ Memory Service │◄───COLD─────►│ CloudSQL │ │
│ │ (Runner) │ ~100ms │ MySQL │ │
│ │ │ │ (Memory data) │ │
│ │ • add_memory │ │ • Analytics │ │
│ │ • get_memories │ │ • Long-term │ │
│ │ • search │ │ • Full-text │ │
│ └─────────────────┘ └──────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────┘
Strategy: - Redis: Hot data (sessions) - High frequency, short TTL, need speed - CloudSQL: Cold data (memories) - Low frequency, long-term storage, need analytics
🚀 Quick Start (Local Development)¶
1. Start Redis (Docker)¶
# Redis 7 Alpine (lightweight)
docker run -d \
--name ifriend-redis \
-p 6379:6379 \
redis:7-alpine
# Verify
docker ps | grep redis
redis-cli ping # Should return: PONG
2. Configure Environment¶
# Copy example
cp .env.redis.example .env
# Edit .env
SESSION_BACKEND=redis
MEMORY_BACKEND=cloudsql
REDIS_URL=redis://localhost:6379/0
REDIS_SESSION_TTL=3600
3. Run Slack Bot¶
# Install dependencies (if not done)
pip install -r ifriend_agent/requirements.txt
# Run
python slack_bot.py
# Expected logs:
# ✅ SessionService inicializado
# ⚡ Redis: Performance otimizada (~10ms vs ~800ms CloudSQL)
4. Test in Slack¶
You: @IFriendBot busca tours em Paris
Bot: [responds in ~500ms instead of ~3s]
# Verify Redis keys
redis-cli KEYS "session:*"
redis-cli LLEN "session:slack_C123_U456_T789:events"
redis-cli LRANGE "session:slack_C123_U456_T789:events" 0 -1
☁️ Production Deployment (Google Cloud)¶
Option 1: Google Cloud Memorystore (Recommended)¶
Step 1: Create Memorystore Instance¶
gcloud redis instances create ifriend-redis \
--size=1 \
--region=us-central1 \
--network=default \
--tier=standard \
--redis-version=redis_7_0
# Get IP address
gcloud redis instances describe ifriend-redis \
--region=us-central1 \
--format="get(host)"
# Output: 10.123.45.67
Step 2: Configure VPC Connector (Cloud Run)¶
# Create VPC Connector (if not exists)
gcloud compute networks vpc-access connectors create ifriend-connector \
--region=us-central1 \
--network=default \
--range=10.8.0.0/28
# Verify
gcloud compute networks vpc-access connectors list
Step 3: Update cloudbuild.yaml¶
steps:
# ... existing build steps ...
- name: 'gcr.io/cloud-builders/gcloud'
id: 'deploy-cloud-run'
args:
- 'run'
- 'deploy'
- 'ifriend-slack-bot'
- '--image=gcr.io/$PROJECT_ID/ifriend-slack-bot:$SHORT_SHA'
- '--region=us-central1'
- '--platform=managed'
- '--vpc-connector=ifriend-connector' # ← ADD THIS
- '--set-env-vars=SESSION_BACKEND=redis,REDIS_URL=redis://10.123.45.67:6379/0,REDIS_SESSION_TTL=3600'
- '--set-env-vars=MEMORY_BACKEND=cloudsql,CLOUDSQL_UNIX_SOCKET=/cloudsql/...'
Step 4: Deploy¶
gcloud builds submit --config cloudbuild.slack.yaml
# Verify logs
gcloud run logs read ifriend-slack-bot --limit 50 | grep Redis
# Should see: "RedisSessionService inicializado"
Option 2: Redis Cloud (Alternative)¶
# 1. Create account: https://redis.com/try-free/
# 2. Get connection URL: rediss://default:password@host:port
# 3. Update .env
REDIS_URL=rediss://default:xxxxx@redis-12345.c123.us-east-1-1.ec2.cloud.redislabs.com:12345
🔍 Redis Schema¶
Session Keys¶
session:{session_id}:meta → HASH
- app_name
- user_id
- created_at
session:{session_id}:events → LIST
- [event1_json, event2_json, ...]
- RPUSH for O(1) append
- LRANGE for get_session
Memory Keys (if using Redis for memory)¶
memory:{app_name}:{user_id}:{memory_id} → HASH
- id, session_id, user_id, created_at, events_json
memory:index:{app_name}:{user_id} → ZSET
- Scored by timestamp for time-range queries
📊 Monitoring¶
Redis CLI Commands¶
# Connect
redis-cli
# Check sessions
KEYS "session:*"
LLEN "session:slack_C123_U456_T789:events"
HGETALL "session:slack_C123_U456_T789:meta"
# Check memory usage
INFO memory
# Monitor live
MONITOR
Cloud Logging Queries¶
-- Redis initialization
resource.type="cloud_run_revision"
jsonPayload.message=~"RedisSessionService inicializado"
-- Performance metrics
resource.type="cloud_run_revision"
jsonPayload.message=~"⚡ Redis"
-- Latency tracking
resource.type="cloud_run_revision"
jsonPayload.message=~"Processamento.*ms"
Expected Metrics¶
- Session Operations: 2-10ms
- Total Latency (Slack message → response): 300-800ms
- LLM: ~200-500ms (unchanged)
- Session: ~10ms (was ~800ms)
- Network: ~50-100ms
- Total: ~260-610ms (vs previous ~1050-1400ms)
🐛 Troubleshooting¶
Issue: "Cannot connect to Redis"¶
# Check REDIS_URL format
REDIS_URL=redis://localhost:6379/0 # ✅ Correct
REDIS_URL=localhost:6379 # ❌ Missing scheme
# Test connection
redis-cli -u $REDIS_URL ping
# Cloud Run: Check VPC connector
gcloud run services describe ifriend-slack-bot \
--region=us-central1 \
--format="get(spec.template.spec.containers[0].env)"
Issue: "Redis connection timeout"¶
# Verify network rules
gcloud compute firewall-rules list | grep redis
# Check Memorystore status
gcloud redis instances list
# Test from Cloud Shell (same VPC)
gcloud compute ssh instance-name
redis-cli -h 10.123.45.67 ping
Issue: "Session not persisting"¶
# Check TTL
redis-cli TTL "session:slack_C123_U456_T789:meta"
# If -1: no expiration set (bug)
# If -2: key doesn't exist
# If >0: expires in N seconds (correct)
# Verify REDIS_SESSION_TTL env var
echo $REDIS_SESSION_TTL # Should be 3600 or similar
Fallback to CloudSQL¶
# If Redis unavailable, switch backend
SESSION_BACKEND=cloudsql # Slower but works
MEMORY_BACKEND=cloudsql
# Or inmemory for dev
SESSION_BACKEND=inmemory # No persistence
🧪 Testing¶
Load Test Script¶
import asyncio
import time
from ifriend_agent.config.backends import get_session_service
async def test_performance():
session_service = get_session_service("redis")
# Create session
session = await session_service.create_session(
app_name="test",
user_id="test_user"
)
# Test append_event performance
events = []
for i in range(100):
start = time.perf_counter()
await session_service.append_event(
session.id,
{"type": "text", "text": f"Event {i}"}
)
elapsed_ms = (time.perf_counter() - start) * 1000
events.append(elapsed_ms)
print(f"Average append_event: {sum(events)/len(events):.2f}ms")
print(f"P50: {sorted(events)[50]:.2f}ms")
print(f"P95: {sorted(events)[95]:.2f}ms")
print(f"P99: {sorted(events)[99]:.2f}ms")
asyncio.run(test_performance())
Expected Results (Redis): - Average: 2-5ms - P95: 8-12ms - P99: 15-20ms
Previous Results (CloudSQL): - Average: 150-200ms - P95: 300-500ms - P99: 800-1200ms
💡 Tips¶
1. TTL Strategy¶
# Short TTL for sessions (ephemeral)
REDIS_SESSION_TTL=3600 # 1 hour
# Long TTL for memory (if using Redis)
REDIS_MEMORY_TTL_DAYS=30 # 30 days
2. Connection Pooling¶
Redis client uses connection pool automatically:
# redis_session_service.py
self.redis = redis.asyncio.from_url(
redis_url,
decode_responses=True,
max_connections=10 # Pool size
)
3. Pipeline for Atomicity¶
# Create session atomically
async with self.redis.pipeline(transaction=True) as pipe:
pipe.hset(meta_key, mapping=meta)
pipe.lpush(events_key, "{}") # Init empty
pipe.expire(meta_key, ttl)
pipe.expire(events_key, ttl)
await pipe.execute()
4. Monitor Memory Usage¶
# Memorystore console
gcloud redis instances describe ifriend-redis \
--region=us-central1 \
--format="get(currentLocationId,memorySizeGb)"
# If approaching limit, reduce TTL or increase size
📈 Next Steps¶
- Deploy to production with Memorystore
- Monitor latency in Cloud Logging
- Compare metrics before/after Redis
- Adjust TTL based on usage patterns
- Consider Redis for Memory too (optional, if analytics not needed)
🎯 Success Criteria¶
- ✅ Session operations < 10ms average
- ✅ Total response time < 1s (vs previous 2-3s)
- ✅ No session loss during bot restarts
- ✅ TTL cleanup automatic (no manual pruning)
- ✅ Cloud Logs show "Redis: Performance otimizada"
Documentation References: - Google Cloud Memorystore - Redis Python Client - ADK Session Service