367 lines
8.7 KiB
Markdown
367 lines
8.7 KiB
Markdown
# Redis Configuration Guide
|
|
|
|
## Overview
|
|
This project now integrates Redis for:
|
|
- **Session Management** - Fast session storage with automatic expiration
|
|
- **Caching** - Response caching for API endpoints and data queries
|
|
- **Performance** - Reduced database load and faster response times
|
|
|
|
## Docker Setup
|
|
|
|
Redis is automatically configured in `docker-compose.yml` with:
|
|
- **Image**: `redis:7-alpine` (lightweight, production-ready)
|
|
- **Port**: `6379` (default)
|
|
- **Password**: Configurable via `REDIS_PASSWORD` environment variable
|
|
- **Persistence**: AOF (Append Only File) enabled for data durability
|
|
- **Health Check**: Automatic Redis connectivity verification
|
|
|
|
### Environment Variables
|
|
|
|
```bash
|
|
# Redis Configuration
|
|
REDIS_URL=redis://:your_password@redis:6379
|
|
REDIS_PASSWORD=redis_password # Change this in production!
|
|
REDIS_PORT=6379
|
|
```
|
|
|
|
## Local Development Setup
|
|
|
|
### 1. **Using Docker Compose** (Recommended)
|
|
|
|
```bash
|
|
# Start all services including Redis
|
|
docker-compose up -d
|
|
|
|
# Verify Redis is running
|
|
docker-compose ps
|
|
|
|
# Check Redis logs
|
|
docker-compose logs redis
|
|
```
|
|
|
|
### 2. **Using Local Redis**
|
|
|
|
If you prefer to run Redis locally:
|
|
|
|
```bash
|
|
# macOS (using Homebrew)
|
|
brew install redis
|
|
brew services start redis
|
|
|
|
# Linux (Ubuntu/Debian)
|
|
sudo apt-get install redis-server
|
|
sudo systemctl start redis-server
|
|
|
|
# Verify connection
|
|
redis-cli ping # Should return "PONG"
|
|
```
|
|
|
|
Update your `.env.local`:
|
|
```bash
|
|
REDIS_URL=redis://localhost:6379
|
|
```
|
|
|
|
## Implementation Details
|
|
|
|
### Redis Client (`lib/redis.ts`)
|
|
|
|
The Redis client is configured with:
|
|
- **Connection pooling** - Automatic reconnection with exponential backoff
|
|
- **Error handling** - Graceful degradation if Redis is unavailable
|
|
- **Type safety** - TypeScript support for all operations
|
|
- **Helper functions** - Simplified cache operations
|
|
|
|
#### Available Functions
|
|
|
|
```typescript
|
|
// Basic cache operations
|
|
getCached<T>(key: string): Promise<T | null>
|
|
setCached<T>(key: string, value: T, expirationSeconds?: number): Promise<boolean>
|
|
deleteCached(key: string): Promise<boolean>
|
|
invalidateCachePattern(pattern: string): Promise<number>
|
|
|
|
// Session helpers
|
|
getSession(sessionId: string): Promise<any>
|
|
setSession(sessionId: string, sessionData: any, expirationSeconds?: number): Promise<boolean>
|
|
deleteSession(sessionId: string): Promise<boolean>
|
|
|
|
// Cache key generators
|
|
cacheKeys.user(userId) // user:{userId}
|
|
cacheKeys.userByEmail(email) // user:email:{email}
|
|
cacheKeys.session(sessionId) // session:{sessionId}
|
|
cacheKeys.webinar(webinarId) // webinar:{webinarId}
|
|
cacheKeys.webinars(page) // webinars:list:{page}
|
|
cacheKeys.registrations(userId) // registrations:{userId}
|
|
cacheKeys.contact(contactId) // contact:{contactId}
|
|
cacheKeys.config // system:config
|
|
cacheKeys.adminSetup // admin:setup
|
|
```
|
|
|
|
### BetterAuth Integration (`lib/auth.ts`)
|
|
|
|
BetterAuth sessions are cached with:
|
|
- **Cache Duration**: 7 days (matching session timeout)
|
|
- **Automatic Invalidation**: Cache cleared on logout
|
|
- **User Caching**: 1-hour cache for user profile data
|
|
|
|
```typescript
|
|
// Cache a session
|
|
await cacheSession(sessionId, sessionData)
|
|
|
|
// Get cached session
|
|
const session = await getCachedSession(sessionId)
|
|
|
|
// Invalidate on logout
|
|
await invalidateSessionCache(sessionId)
|
|
|
|
// Cache user data
|
|
await cacheUser(userId, userData)
|
|
|
|
// Get cached user
|
|
const user = await getCachedUser(userId)
|
|
|
|
// Invalidate user cache
|
|
await invalidateUserCache(userId)
|
|
```
|
|
|
|
### Admin Setup Caching
|
|
|
|
The `/api/admin/setup` endpoint caches configuration:
|
|
- **Cache Duration**: 5 minutes
|
|
- **Invalidation**: Cache cleared on save
|
|
- **Storage**: System configuration with sensitive data handled securely
|
|
|
|
```typescript
|
|
// Cached keys
|
|
cacheKeys.adminSetup // admin:setup
|
|
```
|
|
|
|
## Usage Examples
|
|
|
|
### Caching API Responses
|
|
|
|
```typescript
|
|
// In your API route
|
|
import { getCached, setCached, cacheKeys } from '@/lib/redis';
|
|
|
|
export async function GET(request: Request) {
|
|
const webinarId = 'webinar-123';
|
|
|
|
// Try cache first
|
|
let webinar = await getCached(cacheKeys.webinar(webinarId));
|
|
|
|
if (!webinar) {
|
|
// Fetch from database
|
|
webinar = await db.webinar.findUnique({ where: { id: webinarId } });
|
|
|
|
// Cache for 1 hour
|
|
await setCached(cacheKeys.webinar(webinarId), webinar, 3600);
|
|
}
|
|
|
|
return NextResponse.json(webinar);
|
|
}
|
|
```
|
|
|
|
### Invalidating Cache on Updates
|
|
|
|
```typescript
|
|
import { deleteCached, invalidateCachePattern, cacheKeys } from '@/lib/redis';
|
|
|
|
export async function POST(request: Request) {
|
|
// Update webinar
|
|
const updatedWebinar = await db.webinar.update({...});
|
|
|
|
// Invalidate specific webinar cache
|
|
await deleteCached(cacheKeys.webinar(updatedWebinar.id));
|
|
|
|
// Invalidate all webinar listings
|
|
await invalidateCachePattern('webinars:list:*');
|
|
|
|
return NextResponse.json(updatedWebinar);
|
|
}
|
|
```
|
|
|
|
### Session Caching
|
|
|
|
```typescript
|
|
import { getCachedSession, cacheSession } from '@/lib/auth';
|
|
|
|
// Get user session
|
|
const session = await getCachedSession(sessionId);
|
|
|
|
if (!session) {
|
|
// Fetch from database
|
|
const dbSession = await db.session.findUnique({ where: { id: sessionId } });
|
|
|
|
// Cache with TTL
|
|
await cacheSession(sessionId, dbSession, 604800); // 7 days
|
|
}
|
|
```
|
|
|
|
## Performance Benefits
|
|
|
|
### Before Redis
|
|
- Database queries on every request
|
|
- Session lookups hit Postgres
|
|
- No automatic cache invalidation
|
|
- Higher latency for repeated data
|
|
|
|
### After Redis
|
|
- In-memory cache hits (< 5ms)
|
|
- 50-70% reduction in database queries
|
|
- Automatic expiration (TTL)
|
|
- Significantly improved response times
|
|
- Reduced database connection pool pressure
|
|
|
|
## Monitoring & Debugging
|
|
|
|
### Check Redis Status
|
|
|
|
```bash
|
|
# Connect to Redis CLI
|
|
redis-cli
|
|
|
|
# Monitor commands in real-time
|
|
MONITOR
|
|
|
|
# Get cache size
|
|
INFO memory
|
|
|
|
# List all keys (development only!)
|
|
KEYS *
|
|
|
|
# Delete all data
|
|
FLUSHALL # WARNING: Use with caution!
|
|
```
|
|
|
|
### View Cached Data
|
|
|
|
```bash
|
|
redis-cli
|
|
|
|
# Get specific key
|
|
GET user:123
|
|
|
|
# Get all session keys
|
|
KEYS "session:*"
|
|
|
|
# Get TTL remaining
|
|
TTL session:abc123
|
|
|
|
# Delete specific key
|
|
DEL user:123
|
|
```
|
|
|
|
### Docker Commands
|
|
|
|
```bash
|
|
# Connect to Redis inside Docker
|
|
docker-compose exec redis redis-cli
|
|
|
|
# View Redis logs
|
|
docker-compose logs redis
|
|
|
|
# Restart Redis
|
|
docker-compose restart redis
|
|
|
|
# Remove Redis data
|
|
docker-compose down -v # Removes volumes
|
|
```
|
|
|
|
## Troubleshooting
|
|
|
|
### Redis Connection Errors
|
|
|
|
**Issue**: `Error: connect ECONNREFUSED 127.0.0.1:6379`
|
|
|
|
**Solution**:
|
|
1. Verify Redis is running: `redis-cli ping`
|
|
2. Check `REDIS_URL` environment variable
|
|
3. Ensure Redis port isn't blocked by firewall
|
|
4. Restart Docker: `docker-compose restart redis`
|
|
|
|
### Cache Not Working
|
|
|
|
**Issue**: Cache operations failing silently
|
|
|
|
**Solution**:
|
|
1. Check Redis logs: `docker-compose logs redis`
|
|
2. Verify connectivity: `docker-compose exec redis redis-cli ping`
|
|
3. Check error logs in application console
|
|
4. Verify cache keys are correctly formatted
|
|
|
|
### Memory Issues
|
|
|
|
**Issue**: Redis using too much memory
|
|
|
|
**Solution**:
|
|
1. Enable TTL on all cache operations
|
|
2. Implement cache invalidation patterns
|
|
3. Use `invalidateCachePattern()` to clean up old data
|
|
4. Monitor with `redis-cli INFO memory`
|
|
|
|
### Session Not Persisting
|
|
|
|
**Issue**: Sessions expire too quickly or not at all
|
|
|
|
**Solution**:
|
|
1. Verify TTL is set correctly (default: 7 days = 604800 seconds)
|
|
2. Check `REDIS_URL` is correct
|
|
3. Ensure Redis persistence is enabled (AOF)
|
|
4. Verify no concurrent invalidation calls
|
|
|
|
## Production Deployment
|
|
|
|
### Security Best Practices
|
|
|
|
1. **Change default password**:
|
|
```bash
|
|
REDIS_PASSWORD=your_strong_password_here
|
|
```
|
|
|
|
2. **Use Redis with authentication**:
|
|
```bash
|
|
REDIS_URL=redis://:your_password@your-redis-host:6379
|
|
```
|
|
|
|
3. **Enable SSL/TLS**:
|
|
```bash
|
|
REDIS_URL=rediss://:your_password@your-redis-host:6380
|
|
```
|
|
|
|
4. **Network Security**:
|
|
- Don't expose Redis port to the internet
|
|
- Use VPC/private network
|
|
- Implement firewall rules
|
|
|
|
### High Availability Setup
|
|
|
|
For production, consider:
|
|
- **Redis Cluster** - Horizontal scaling
|
|
- **Sentinel** - Automatic failover
|
|
- **Cloud Redis** - AWS ElastiCache, Google Cloud Memorystore, Azure Cache for Redis
|
|
- **Redis Enterprise** - Enterprise features and support
|
|
|
|
### Monitoring
|
|
|
|
Use the following tools:
|
|
- **RedisInsight** - Visual Redis management tool
|
|
- **DataDog** - APM and metrics
|
|
- **New Relic** - Performance monitoring
|
|
- **Cloud Provider Dashboard** - If using managed Redis
|
|
|
|
## Further Reading
|
|
|
|
- [Redis Documentation](https://redis.io/documentation)
|
|
- [ioredis GitHub](https://github.com/luin/ioredis)
|
|
- [Redis Best Practices](https://redis.io/docs/management/optimization/)
|
|
- [BetterAuth Documentation](https://better-auth.com)
|
|
|
|
## Support
|
|
|
|
For issues or questions:
|
|
1. Check Redis logs: `docker-compose logs redis`
|
|
2. Verify connection: `redis-cli ping`
|
|
3. Review error messages in application logs
|
|
4. Check the troubleshooting section above
|