Caching — In-Memory & Redis

লেসন ৩৭ / ৫০

কেন Cache?

Repeat DB call থেকে latency কমায় — popular page 100x দ্রুত।

In-Memory Cache

// Program.cs
builder.Services.AddMemoryCache();

// Service
public class ProductService {
    private readonly IMemoryCache _cache;
    private readonly AppDbContext _ctx;

    public ProductService(IMemoryCache c, AppDbContext ctx) {
        _cache = c; _ctx = ctx;
    }

    public async Task<Product?> GetAsync(int id) {
        var key = $"product_{id}";
        if (_cache.TryGetValue(key, out Product cached)) return cached;

        var p = await _ctx.Products.FindAsync(id);
        if (p != null) {
            _cache.Set(key, p, new MemoryCacheEntryOptions {
                AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10),
                SlidingExpiration = TimeSpan.FromMinutes(2)
            });
        }
        return p;
    }
}

GetOrCreate Helper

var product = await _cache.GetOrCreateAsync($"product_{id}", async entry => {
    entry.AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10);
    return await _ctx.Products.FindAsync(id);
});

Cache Invalidation

public async Task UpdateAsync(Product p) {
    _ctx.Products.Update(p);
    await _ctx.SaveChangesAsync();
    _cache.Remove($"product_{p.Id}");   // ⭐ stale data এড়ান
}

Distributed Cache — Redis ⭐

// Multi-server scenario — সবাই same cache দেখে
dotnet add package Microsoft.Extensions.Caching.StackExchangeRedis

// Program.cs
builder.Services.AddStackExchangeRedisCache(opt => {
    opt.Configuration = builder.Configuration.GetConnectionString("Redis");
    opt.InstanceName = "MyApp_";
});

IDistributedCache Usage

public class ProductService {
    private readonly IDistributedCache _cache;

    public async Task<Product?> GetAsync(int id) {
        var key = $"product:{id}";
        var json = await _cache.GetStringAsync(key);
        if (json != null) return JsonSerializer.Deserialize<Product>(json);

        var p = await _ctx.Products.FindAsync(id);
        if (p != null) {
            await _cache.SetStringAsync(key, JsonSerializer.Serialize(p),
                new DistributedCacheEntryOptions {
                    AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(15)
                });
        }
        return p;
    }
}

Output Caching (.NET 7+)

builder.Services.AddOutputCache(opt => {
    opt.AddBasePolicy(b => b.Expire(TimeSpan.FromSeconds(60)));
});
app.UseOutputCache();

// Controller
[OutputCache(Duration = 60, VaryByQueryKeys = new[] { "page" })]
[HttpGet]
public async Task<IActionResult> List(int page) => ...

Caching Strategies

  • Cache-Aside — App cache check, miss হলে DB
  • Write-Through — Update-এ cache + DB একসাথে
  • Refresh-Ahead — Expire-এর আগে background refresh