Overview
The caching system consists of:- Redis: In-memory data store for distributed caching
- ICacheService: Abstraction for cache operations
- QueryCachingBehavior: MediatR pipeline behavior for automatic query caching
- ICachedQuery: Interface to mark queries as cacheable
Redis Setup
Configuration
Redis connection string is configured inappsettings.Development.json:
Docker Compose Configuration
Redis runs as a containerized service:Dependency Injection
Caching is configured in src/Bookify.Infrastructure/DependencyInjection.cs:129:Cache Service
ICacheService Interface
The cache abstraction (src/Bookify.Application/Abstractions/Caching/ICacheService.cs:4) provides three core operations:Implementation
TheCacheService (src/Bookify.Infrastructure/Caching/CacheService.cs:8) wraps Redis operations:
Cache Options
Cache expiration is configured in src/Bookify.Infrastructure/Caching/CacheOptions.cs:7:Automatic Query Caching
QueryCachingBehavior
Bookify uses a MediatR pipeline behavior (src/Bookify.Application/Abstractions/Behaviors/QueryCachingBehavior.cs:8) to automatically cache query results:How It Works
Creating Cached Queries
ICachedQuery Interface
To enable caching for a query, implement theICachedQuery interface (src/Bookify.Application/Abstractions/Caching/ICachedQuery.cs:5):
Example: Cached Query
Here’s how to create a cached query:- CacheKey: Must be unique for each query variation. Include query parameters.
- Expiration: Optional. If
null, defaults to 1 minute.
Manual Caching
For scenarios where automatic caching doesn’t fit, useICacheService directly:
Cache Invalidation
When data changes, invalidate the cache to ensure consistency:Invalidation Strategies
Explicit Invalidation
Explicit Invalidation
Manually remove cache entries when data changes. Most precise but requires careful tracking.
Time-Based Expiration
Time-Based Expiration
Let cache entries expire automatically. Simpler but may serve stale data.
Pattern-Based Invalidation
Pattern-Based Invalidation
For complex scenarios, use cache key patterns with wildcards (requires additional Redis commands).
Best Practices
Cache Key Design
- Use descriptive, hierarchical keys:
entity-id-operation - Include all query parameters that affect results
- Keep keys short but meaningful
- Avoid special characters
Expiration Strategy
- Short TTL for frequently changing data (1-5 minutes)
- Longer TTL for static data (hours/days)
- Consider business requirements
- Balance freshness vs. performance
What to Cache
- Query results that are expensive to compute
- Data that doesn’t change frequently
- Aggregations and statistics
- API responses from external services
What NOT to Cache
- User-specific sensitive data
- Data that changes frequently
- Large objects (>1MB)
- Transaction results
Monitoring
Monitor cache performance through:-
Application Logs: Cache hits and misses are logged by
QueryCachingBehavior -
Redis CLI: Connect to Redis to inspect keys and memory usage
- Health Checks: Verify Redis connectivity (see Health Checks)
Troubleshooting
Cache Not Working
- Verify Redis is running:
docker ps | grep redis - Check connection string in appsettings
- Ensure query implements
ICachedQuery - Check logs for exceptions
Stale Data Issues
- Reduce cache expiration time
- Implement explicit cache invalidation
- Review cache key uniqueness
Memory Issues
- Monitor Redis memory usage
- Reduce expiration times
- Implement cache eviction policies
- Consider caching smaller objects
Next Steps
Database Setup
Configure PostgreSQL and EF Core
Health Checks
Monitor Redis availability