Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Redis Caching Setup

Overview

The Pricing Engine uses Redis for caching pricing rules lookups. This provides significant performance improvements for high-traffic API endpoints by reducing database queries.

Configuration

Environment Variables

VariableDescriptionDefault
REDIS_URLRedis connection URLredis://localhost:6379/0

Cache Settings

SettingProductionDevelopment
Namespacepricing_engine:productionpricing_engine:development
Connect Timeout1 second1 second
Read Timeout200ms200ms
Write Timeout200ms200ms
Reconnect Attempts21
CompressionEnabledEnabled

Docker Setup

Redis is already configured in docker-compose.yml:

redis:
  image: redis:7-alpine
  ports:
    - "6379:6379"
  volumes:
    - redis_data:/data

The Rails app is configured with:

REDIS_URL: "redis://redis:6379/0"

Cached Data

Repository Methods

The following PricingRuleRepository methods are cached:

MethodCache Key PatternTTL
active_rules_for(restaurant_id)pricing_rules:v1:restaurant:{id}:active_rules5 min
rules_for_date(restaurant_id, date)pricing_rules:v1:restaurant:{id}:rules_for_date:{date}5 min
rules_for_package(restaurant_id, package_id)pricing_rules:v1:restaurant:{id}:rules_for_package:{pkg}5 min

Caching Strategy

  1. Cache IDs Only: We cache only record IDs, not full ActiveRecord objects, to avoid serialization issues and memory bloat.

  2. Reload with Eager Loading: After fetching cached IDs, we reload records from the database with eager-loaded associations.

  3. Restaurant-Scoped Keys: All cache keys are scoped by restaurant_id for targeted invalidation.

  4. Graceful Degradation: If Redis is unavailable, the app falls back to direct database queries with logging.

Cache Invalidation

Cache is automatically invalidated when:

  • A PricingRule is created, updated, or destroyed
  • A DateTimeCondition is created, updated, or destroyed
  • A ConditionItem is created, updated, or destroyed
  • A PricingRulesPackage is created, updated, or destroyed

All invalidation uses after_commit callbacks to ensure database changes are complete before cache is cleared.

Manual Invalidation

# Invalidate cache for a specific restaurant
PricingRuleRepository.invalidate_restaurant_cache("restaurant_123")

# Invalidate all pricing rule caches
PricingRuleRepository.invalidate_all_caches

Bypass Cache

To skip caching for a specific query:

PricingRuleRepository.active_rules_for("restaurant_123", skip_cache: true)
PricingRuleRepository.rules_for_date("restaurant_123", Date.current, skip_cache: true)
PricingRuleRepository.rules_for_package("restaurant_123", "package_456", skip_cache: true)

Development

Enable Caching in Development

By default, caching is disabled in development. To enable:

rails dev:cache

This creates tmp/caching-dev.txt and enables Redis caching.

Disable Caching in Development

rails dev:cache

Running the command again removes the file and disables caching.

View Cache Stats (in rails console)

# Check if Redis is connected
Rails.cache.redis.ping

# Get Redis info
Rails.cache.redis.info

# Clear all cache
Rails.cache.clear

Testing

Tests use :null_store for the cache, which doesn’t persist data. This ensures tests are isolated and don’t depend on cache state.

To test caching behavior specifically:

# In spec
allow(Rails.cache).to receive(:fetch).and_call_original
# or
Rails.cache.clear

Performance Considerations

  1. TTL Balance: 5-minute TTL balances freshness with cache hit rate. Adjust based on update frequency.

  2. Memory Usage: Redis memory is bounded by eviction policies. Monitor with redis-cli info memory.

  3. Connection Pool: Production uses connection pooling (default pool size: 5).

  4. Timeouts: Short timeouts (200ms) prevent slow Redis from degrading app performance.

Monitoring

Key Metrics to Monitor

  • Cache hit rate (Rails.cache.stats if using redis-store)
  • Redis memory usage
  • Redis connection count
  • Cache invalidation frequency

Logging

Cache errors are logged with the [Redis Cache Error] prefix:

[Redis Cache Error] method=read returning=nil error=Redis::CannotConnectError: Connection refused

Cache invalidation errors use [Cache Invalidation Error]:

[Cache Invalidation Error] restaurant_id=rest_123 error=Redis::CommandError: NOSCRIPT