Real World Example

Big Billion Days Product Pages

During sales like Flipkart's Big Billion Days, millions of users open the same few hundred product pages. Shops use two cache levels: a tiny Caffeine cache (a few seconds) in each instance in front of Redis (minutes), with eviction when a seller changes a price.

@Service
@RequiredArgsConstructor
public class ProductCatalog {
    private final ProductRepository repo;
    private final CacheManager redisCacheManager;
    private final Cache<Long, ProductDto> local = Caffeine.newBuilder()
            .maximumSize(5_000).expireAfterWrite(Duration.ofSeconds(5)).build();

    public ProductDto get(Long id) {
        return local.get(id, key -> {
            var redis = redisCacheManager.getCache("products");
            ProductDto cached = redis.get(key, ProductDto.class);
            if (cached != null) return cached;
            ProductDto fresh = repo.findById(key).map(ProductDto::from).orElseThrow();
            redis.put(key, fresh);
            return fresh;
        });
    }

    @Transactional
    public ProductDto changePrice(Long id, BigDecimal price) {
        Product p = repo.findById(id).orElseThrow();
        p.setPrice(price);
        redisCacheManager.getCache("products").evict(id);
        local.invalidate(id);
        return ProductDto.from(p);
    }
}

Example

Effect measured with a load test:

wrk -t4 -c200 -d30s http://localhost:8080/api/products/3

Example output

             no cache     Redis only    Caffeine + Redis
p50 latency  48 ms        6 ms          1.2 ms
req/sec      3,900        21,000        58,000
DB queries   117,000      14            14

Note: Other instances keep their local copy for up to 5 seconds after a price change. That is acceptable for a product page, but the checkout always reads price and stock from the database.