Cache overview
Redis serves as a write-through cache for exchange rates and supported currencies:- Exchange rates: Cached for 5 minutes
- Supported currencies: Cached for 24 hours
- Data format: JSON with
Decimalserialized as strings for precision
Write-through caching means every fresh fetch immediately populates Redis, so the second request for any pair within the TTL window returns instantly.
Cache key structure
The cache uses a hierarchical key naming scheme for clarity and organization. Frominfrastructure/cache/redis_cache.py:17:
Key patterns
| Pattern | Example | Purpose | TTL |
|---|---|---|---|
rate:{from}:{to} | rate:USD:EUR | Exchange rate data | 5 minutes |
currencies:supported | currencies:supported | List of currency codes | 24 hours |
Using structured keys (colon-separated namespaces) makes debugging easier and enables pattern-based operations like
KEYS rate:* in development.TTL configuration
Time-to-live values are configured in theRedisCacheService constructor.
From infrastructure/cache/redis_cache.py:11:
TTL rationale
| Cache Type | TTL | Reasoning |
|---|---|---|
| Exchange rates | 5 minutes | Balances freshness with API cost. Most real-world use cases don’t require second-level accuracy. |
| Supported currencies | 24 hours | Currency lists rarely change. Long TTL reduces database queries and provider API calls. |
Why 5 minutes for rates?
Why 5 minutes for rates?
Exchange rates update frequently but not instantly. A 5-minute window provides:
- Fresh enough for most applications (e-commerce, travel booking, reporting)
- Cost effective by limiting API calls to 1 per pair per 5 minutes
- Fast responses for repeated conversions during the cache window
Cache operations
Reading from cache
Theget_rate method retrieves cached rates and deserializes them into domain objects.
From infrastructure/cache/redis_cache.py:20:
Decimal is serialized as a string (not float) to preserve precision. Financial calculations require exact decimal arithmetic.Writing to cache
Theset_rate method stores rates with automatic expiration.
From infrastructure/cache/redis_cache.py:39:
Using
setex (SET with EXpiry) atomically sets the value and TTL in a single command, preventing race conditions.Supported currencies cache
Supported currencies are cached as a JSON array. Frominfrastructure/cache/redis_cache.py:52:
Cache integration in rate service
TheRateService checks the cache before fetching from providers.
From application/services/rate_service.py:30:
Cache flow diagram
Why check cache before validating currency?
Why check cache before validating currency?
The validation check itself queries the cache (for supported currencies). By checking rate cache first, you can return immediately if the rate is cached, avoiding even the validation queries.However, in this implementation, validation happens first to ensure early error responses for invalid currencies.
Cache warming
Supported currencies are cached during application startup to avoid cold-start latency. Fromapplication/services/currency_service.py:38:
save_supported_currencies method updates both the database and Redis:
This cache warming ensures the first requests after deployment are just as fast as subsequent requests.
Data serialization
Decimal precision
Financial calculations require exact decimal arithmetic. Floating-point numbers introduce rounding errors.infrastructure/cache/redis_cache.py:45:
DateTime serialization
Datetimes are stored as ISO 8601 strings for readability and compatibility:Cache invalidation
The system uses TTL-based expiration rather than explicit invalidation:- No manual cache invalidation needed
- Redis automatically removes expired keys
- Fresh data is fetched transparently on cache miss
Error handling
Cache errors are wrapped in domain exceptions and logged. Frominfrastructure/cache/redis_cache.py:36:
| Error Type | Cause | Handling |
|---|---|---|
json.JSONDecodeError | Corrupted cache data | Raise CacheError, log error, treat as cache miss |
ConnectionError | Redis unavailable | Propagate exception, return 503 |
TimeoutError | Slow Redis response | Propagate exception, consider fallback to DB |
Cache errors are non-fatal for read operations. If Redis is unavailable, the service can fall back to fetching from providers (at the cost of increased latency).
Performance impact
Cache hit scenario
Cache miss scenario
Cache effectiveness metrics
| Metric | Target | Measurement |
|---|---|---|
| Hit rate | > 80% | cache_hits / total_requests |
| Response time (hit) | < 50ms | P95 latency for cached requests |
| Response time (miss) | < 1000ms | P95 latency for provider fetch |
Configuration best practices
Production TTL
Keep 5-minute default for most use cases. Adjust based on traffic patterns.
Redis memory
Monitor memory usage. Each rate is ~200 bytes. Plan capacity accordingly.
Eviction policy
Use
allkeys-lru to automatically remove least-used keys when memory is full.Persistence
Enable RDB snapshots for cache recovery after restarts.
Monitoring and observability
Key metrics to track:- Cache hit rate by currency pair
- Average response time (cached vs uncached)
- Redis connection pool statistics
- TTL distribution (how long until expiration)
Redis configuration
Connection is established at startup fromapi/dependencies.py:42:
decode_responses=True automatically decodes Redis bytes to strings, simplifying JSON parsing.Recommended Redis settings
Troubleshooting
Cache always returns miss
Cache always returns miss
- Check Redis connection in application logs
- Verify
REDIS_URLenvironment variable - Test Redis connectivity:
redis-cli ping - Check if TTL is set correctly:
redis-cli TTL rate:USD:EUR
Stale rates returned
Stale rates returned
- Verify TTL configuration in
RedisCacheService - Check if rates are being written after provider fetch
- Review logs for cache write errors
- Manually flush keys:
redis-cli DEL rate:USD:EUR
Redis memory growing unbounded
Redis memory growing unbounded
- Set
maxmemoryandmaxmemory-policyin redis.conf - Monitor key count:
redis-cli DBSIZE - Check for keys without TTL:
redis-cli KEYS * | xargs redis-cli TTL - Review eviction stats:
redis-cli INFO stats
