Isaac.

performance

Implementing Caching Strategies

Add effective caching to improve application performance.

By Emem IsaacJuly 1, 20232 min read
#caching#redis#distributed cache#memoization
Share:

A Simple Analogy

Caching is like keeping frequently-used items within reach. Don't go to the warehouse every time; grab from nearby.


Why Cache?

  • Performance: Avoid expensive operations
  • Scalability: Reduce database load
  • UX: Faster response times
  • Cost: Cheaper compute resources
  • Reliability: Handle traffic spikes

In-Memory Cache

public class ProductService
{
    private readonly IMemoryCache _cache;
    private readonly IProductRepository _repository;
    
    public async Task<Product> GetProductAsync(int id)
    {
        var cacheKey = $"product-{id}";
        
        if (_cache.TryGetValue(cacheKey, out var cached))
            return cached as Product;
        
        var product = await _repository.GetAsync(id);
        
        var options = new MemoryCacheEntryOptions()
            .SetAbsoluteExpiration(TimeSpan.FromHours(1))
            .SetSlidingExpiration(TimeSpan.FromMinutes(20));
        
        _cache.Set(cacheKey, product, options);
        
        return product;
    }
}

Distributed Cache (Redis)

public class ProductService
{
    private readonly IDistributedCache _cache;
    private readonly IProductRepository _repository;
    
    public async Task<Product> GetProductAsync(int id)
    {
        var cacheKey = $"product-{id}";
        var cached = await _cache.GetStringAsync(cacheKey);
        
        if (cached != null)
            return JsonSerializer.Deserialize<Product>(cached);
        
        var product = await _repository.GetAsync(id);
        
        await _cache.SetStringAsync(
            cacheKey,
            JsonSerializer.Serialize(product),
            new DistributedCacheEntryOptions
            {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromHours(1)
            }
        );
        
        return product;
    }
}

Cache Invalidation

public async Task UpdateProductAsync(int id, UpdateProductRequest request)
{
    var product = new Product { Id = id, Name = request.Name };
    await _repository.UpdateAsync(product);
    
    // Invalidate cache
    await _cache.RemoveAsync($"product-{id}");
    await _cache.RemoveAsync("products-list");
}

HTTP Caching Headers

[HttpGet("{id}")]
public async Task<IActionResult> GetProduct(int id)
{
    var product = await _service.GetProductAsync(id);
    
    // Cache in browser for 1 hour
    Response.Headers.CacheControl = "public, max-age=3600";
    Response.Headers.ETag = $"\"{product.UpdatedAt.GetHashCode()}\"";
    
    return Ok(product);
}

Best Practices

  1. TTL: Set reasonable expiration times
  2. Invalidation: Remove when data changes
  3. Keys: Use consistent naming
  4. Size: Don't cache too much
  5. Monitoring: Track hit rates

Related Concepts

  • Cache-aside pattern
  • Write-through cache
  • Cache warming
  • Cache stampede

Summary

Implement multi-level caching with in-memory and distributed caches. Use appropriate TTLs and invalidation strategies.

Share:

Written by Emem Isaac

Expert Software Engineer with 15+ years of experience building scalable enterprise applications. Specialized in ASP.NET Core, Azure, Docker, and modern web development. Passionate about sharing knowledge and helping developers grow.

Ready to Build Something Amazing?

Let's discuss your project and explore how my expertise can help you achieve your goals. Free consultation available.

💼 Trusted by 50+ companies worldwide | ⚡ Average response time: 24 hours