Redis is used for high-performance caching and temporary data storage.
{
"Redis": {
"Host": "redis:6379"
}
}Redis__Host=redis:6379Location: src/infrastructure/Persistence/Redis/RedisService.cs
Key Features:
- Connection multiplexing
- Automatic reconnection
- Serialization/deserialization
- TTL (Time To Live) support
public interface IRedisService
{
IDatabase GetDatabase();
Task<bool> SetAsync<T>(string key, T value, TimeSpan? expiry = null);
Task<T?> GetAsync<T>(string key);
Task<bool> DeleteAsync(string key);
Task<bool> ExistsAsync(string key);
}public class ClientController
{
private readonly IRedisService _redisService;
public ClientController(IRedisService redisService)
{
_redisService = redisService;
}
}// Cache for 5 minutes
await _redisService.SetAsync("user:123", userData, TimeSpan.FromMinutes(5));
// Cache forever (no expiration)
await _redisService.SetAsync("config:app", configData);var user = await _redisService.GetAsync<User>("user:123");
if (user == null)
{
// Not in cache, fetch from database
user = await _database.GetUserAsync(123);
await _redisService.SetAsync("user:123", user, TimeSpan.FromMinutes(5));
}
return user;await _redisService.DeleteAsync("user:123");if (await _redisService.ExistsAsync("user:123"))
{
var user = await _redisService.GetAsync<User>("user:123");
}// Store user session
await _redisService.SetAsync(
$"session:{sessionId}",
sessionData,
TimeSpan.FromHours(2)
);
// Retrieve session
var session = await _redisService.GetAsync<SessionData>($"session:{sessionId}");[HttpGet("products")]
public async Task<IActionResult> GetProducts()
{
var cacheKey = "products:all";
// Try cache first
var products = await _redisService.GetAsync<List<Product>>(cacheKey);
if (products != null)
return Ok(products);
// Not in cache, fetch from DB
products = await _database.GetProductsAsync();
// Cache for 10 minutes
await _redisService.SetAsync(cacheKey, products, TimeSpan.FromMinutes(10));
return Ok(products);
}var key = $"ratelimit:{userId}:{DateTime.UtcNow:yyyyMMddHH}";
var count = await _redisService.GetAsync<int>(key);
if (count >= 100)
return StatusCode(429, "Too many requests");
await _redisService.SetAsync(key, count + 1, TimeSpan.FromHours(1));// Store verification code
await _redisService.SetAsync(
$"verify:{email}",
verificationCode,
TimeSpan.FromMinutes(15)
);
// Check verification
var code = await _redisService.GetAsync<string>($"verify:{email}");
if (code == userInputCode)
{
await _redisService.DeleteAsync($"verify:{email}");
// Proceed with verification
}Use consistent naming conventions:
user:{id} → User data
session:{sessionId} → Session data
products:all → All products
product:{id} → Single product
ratelimit:{userId}:{hour} → Rate limiting
verify:{email} → Verification codes
token:{token} → Authentication tokens
Data is automatically serialized to JSON:
// Complex objects work automatically
var user = new User { Id = 1, Name = "John", Email = "john@example.com" };
await _redisService.SetAsync("user:1", user);
var retrieved = await _redisService.GetAsync<User>("user:1");
// retrieved is fully deserialized User object- Use Appropriate TTL: Don't cache forever unless necessary
- Key Naming: Use consistent, hierarchical key names
- Invalidation: Remove stale data when source changes
- Size Limits: Don't cache very large objects
- Fallback: Always have a fallback when cache misses
- Monitoring: Track cache hit/miss rates
// Automatically expires
await _redisService.SetAsync("data", value, TimeSpan.FromMinutes(10));// When data changes, invalidate cache
public async Task UpdateUser(User user)
{
await _database.UpdateUserAsync(user);
await _redisService.DeleteAsync($"user:{user.Id}");
}// Delete all user-related caches
var database = _redisService.GetDatabase();
var server = database.Multiplexer.GetServer(endpoint);
await foreach (var key in server.KeysAsync(pattern: "user:*"))
{
await database.KeyDeleteAsync(key);
}- Batch Operations: Use pipelines for multiple operations
- Connection Reuse: Service handles this automatically
- Async Operations: Always use async methods
- Small Values: Redis is optimized for small, frequent operations
- Compression: Consider compressing large values
Redis container in deploy/docker/dev/docker-compose.yml:
- AOF persistence enabled
- Data volume for persistence
- Health checks configured
Access Redis CLI in Docker:
docker exec -it redis redis-cliUseful Commands:
# List all keys
KEYS *
# Get value
GET user:123
# Check TTL (time to live)
TTL user:123
# Delete key
DEL user:123
# Get all keys matching pattern
KEYS user:*
# Flush all data (careful!)
FLUSHALL
Check Redis status:
# In Redis CLI
INFO
# Memory usage
INFO memory
# Stats
INFO stats
# Connected clients
CLIENT LISTtry
{
await _redisService.SetAsync("key", value);
}
catch (RedisConnectionException ex)
{
_logger.LogError(ex, "Redis connection failed");
// Fallback: proceed without cache
}
catch (RedisTimeoutException ex)
{
_logger.LogError(ex, "Redis operation timeout");
// Fallback logic
}- Verify Redis container is running
- Check network connectivity
- Verify host and port in configuration
- Monitor memory usage
- Check for large keys
- Review TTL settings
- Consider Redis maxmemory policy
- Check AOF/RDB configuration
- Verify volume mounts in Docker
- Review Redis logs