📘 Caching Concepts

Cache-Aside — System Design

Cache-Aside is the most commonly used caching pattern in system design. Reading from the database when cache misses Putting the database result into cache.

S

Site Administrator

📅 Sep 27, 2026 🕒 8 min read 👁 24 views
☆ Save 𝕏 in f ✆
Cache-Aside — System Design

Cache-Aside is the most commonly used caching pattern in system design.

The application itself is responsible for:

  • Checking the cache
  • Reading from the database when cache misses
  • Putting the database result into cache
  • Updating or invalidating the cache when data changes

The simple flow is:

Read:
Application → Cache
              ↓
          Cache Hit → Return
              ↓
          Cache Miss
              ↓
          Database
              ↓
          Update Cache
              ↓
            Return

1. Cache-Aside Architecture

flowchart LR
    U[User] --> A[ASP.NET Core API]
    A --> C[Redis Cache]

    C -->|Cache Hit| A
    C -->|Cache Miss| D[SQL Server]

    D --> C
    C --> A
    A --> U

The cache does not automatically load data from the database.

The application controls the entire process.


2. Cache Hit

Suppose Redis already contains:

product:1 → Laptop, 75000, stock 20

The application receives:

GET /products/1

Flow:

sequenceDiagram
    participant U as User
    participant A as API
    participant C as Redis

    U->>A: GET /products/1
    A->>C: GET product:1
    C-->>A: Product
    A-->>U: Product Response

SQL Server is not contacted.


3. Cache Miss

Suppose Redis does not contain product:1.

sequenceDiagram
    participant U as User
    participant A as API
    participant C as Redis
    participant D as SQL Server

    U->>A: GET /products/1
    A->>C: GET product:1
    C-->>A: MISS
    A->>D: SELECT Product
    D-->>A: Product
    A->>C: SET product:1
    A-->>U: Product Response

This is the key Cache-Aside pattern:

1. Check cache
2. Cache miss
3. Read database
4. Store result in cache
5. Return result

4. Complete SQL Server Example

CREATE DATABASE CacheAsideDemo;
GO

USE CacheAsideDemo;
GO

CREATE TABLE Products
(
    ProductId INT PRIMARY KEY,
    ProductName VARCHAR(100),
    Price DECIMAL(10,2),
    Stock INT
);
GO

INSERT INTO Products
(
    ProductId,
    ProductName,
    Price,
    Stock
)
VALUES
(1, 'Laptop', 75000, 20),
(2, 'Mobile', 30000, 50),
(3, 'Headphones', 3000, 100);
GO

SELECT ProductId, ProductName, Price, Stock
FROM Products
ORDER BY ProductId;
GO

Expected Output

ProductId  ProductName  Price      Stock
---------  -----------  ---------  -----
1          Laptop       75000.00   20
2          Mobile       30000.00   50
3          Headphones   3000.00    100

5. ASP.NET Core Cache-Aside Example

Using Redis through IDistributedCache:

builder.Services.AddStackExchangeRedisCache(options =>
{
    options.Configuration = "localhost:6379";
    options.InstanceName = "Shop:";
});

app.MapGet("/products/{id}", async (
    int id,
    IDistributedCache cache,
    AppDbContext db) =>
{
    string key = $"product:{id}";

    // 1. Check cache
    var cachedProduct = await cache.GetStringAsync(key);

    if (cachedProduct != null)
    {
        return Results.Content(
            cachedProduct,
            "application/json");
    }

    // 2. Cache miss - read database
    var product = await db.Products
        .FirstOrDefaultAsync(x => x.ProductId == id);

    if (product == null)
        return Results.NotFound();

    // 3. Store database result in cache
    var json = JsonSerializer.Serialize(product);

    await cache.SetStringAsync(
        key,
        json,
        new DistributedCacheEntryOptions
        {
            AbsoluteExpirationRelativeToNow =
                TimeSpan.FromMinutes(10)
        });

    // 4. Return response
    return Results.Content(
        json,
        "application/json");
});

6. First Request vs Second Request

First Request

GET /products/1

Redis
  ↓
MISS

SQL Server
  ↓
Laptop

Redis
  ↓
Store product:1

Response
  ↓
User

Second Request

GET /products/1

Redis
  ↓
HIT

Response
  ↓
User

The second request does not need SQL Server.


7. Updating Data — The Important Part

Suppose the product price changes.

Old price:
75000

New price:
70000

If we update only SQL Server:

flowchart LR
    A[API] --> D[SQL Server]
    D -->|Price = 70000| D

    C[Redis] -->|Still 75000| A

Now the cache contains stale data.

This is called a cache consistency problem.


8. Cache-Aside Write Pattern

A common approach is:

sequenceDiagram
    participant U as User
    participant A as API
    participant D as SQL Server
    participant C as Redis

    U->>A: Update Product
    A->>D: UPDATE Product
    D-->>A: Success
    A->>C: DELETE product:1
    C-->>A: Deleted
    A-->>U: Success

Instead of updating the cache with the new value, we can invalidate it.

The next read will populate the cache again.

UPDATE Database
      ↓
DELETE Cache
      ↓
Next Read
      ↓
Cache Miss
      ↓
Read Database
      ↓
Populate Cache

9. Complete Update Example

app.MapPut("/products/{id}", async (
    int id,
    Product request,
    IDistributedCache cache,
    AppDbContext db) =>
{
    var product = await db.Products
        .FirstOrDefaultAsync(x => x.ProductId == id);

    if (product == null)
        return Results.NotFound();

    // Update database
    product.ProductName = request.ProductName;
    product.Price = request.Price;
    product.Stock = request.Stock;

    await db.SaveChangesAsync();

    // Invalidate cache
    string key = $"product:{id}";

    await cache.RemoveAsync(key);

    return Results.Ok(product);
});

Now:

Database updated
      ↓
Cache deleted
      ↓
Next GET
      ↓
Cache MISS
      ↓
Read latest database value
      ↓
Populate cache

10. Why Delete Cache Instead of Updating It?

Updating both database and cache introduces more possibilities for inconsistency.

Database update succeeds
Cache update fails

Result:
Database = new value
Cache = old value

With invalidation:

Database update
      ↓
Delete cache
      ↓
Next request rebuilds cache

This is a common Cache-Aside approach, although production systems still need to consider failures between the database update and cache invalidation.


11. Cache-Aside with Multiple API Servers

flowchart LR
    U[Users] --> LB[Load Balancer]

    LB --> A1[API Server 1]
    LB --> A2[API Server 2]
    LB --> A3[API Server 3]

    A1 --> R[Redis]
    A2 --> R
    A3 --> R

    R --> DB[SQL Server]

All API servers share Redis.

For example:

Server 1 → Redis → product:1
Server 2 → Redis → product:1
Server 3 → Redis → product:1

Therefore the cache does not depend on which API server receives the request.


12. What Happens If Redis Goes Down?

A Cache-Aside application should treat the cache as an optimization, not the only source of data.

flowchart TD
    A[API] --> C[Redis]
    C -->|Available| H[Cache Hit]
    C -->|Unavailable| F[Fallback]
    F --> D[SQL Server]
    D --> A

If Redis is unavailable:

Request
   ↓
Redis unavailable
   ↓
Read SQL Server
   ↓
Return response

However, if millions of requests simultaneously fall back to SQL Server, the database can become overloaded.

This leads to problems such as cache stampede, which we will cover later.


13. Advantages of Cache-Aside

  • Simple to understand
  • Application controls caching
  • Only frequently accessed data needs to be cached
  • Database remains the source of truth
  • Works well with Redis
  • Easy to introduce incrementally
  • Cache failure can be handled with database fallback

14. Disadvantages

  • Application code becomes responsible for caching
  • Cache invalidation is difficult
  • First request after expiration is slower
  • Stale data is possible
  • Cache miss storms can overload the database
  • Read/write consistency needs careful design

15. Cache-Aside vs No Cache

Without Cache Cache-Aside
Every read may hit database Application checks cache first
Higher database load Lower database load
Simple consistency Cache invalidation required
Higher latency for repeated reads Lower latency for cache hits

16. Real-World Scenario — Product Catalog

Suppose an e-commerce system receives:

100,000 product-read requests/sec

Most requests are for popular products.

Using Cache-Aside:

flowchart LR
    U[100K Requests/sec] --> API[Product API]
    API --> R[Redis]

    R -->|95% Hit| H[95K Responses]
    R -->|5% Miss| DB[SQL Server]

    DB --> R

Approximately 95% of reads can be served from Redis in this example.

SQL Server receives only the cache misses rather than every request.


17. When Should We Use Cache-Aside?

Cache-Aside is a good fit when:

  • Application has many reads
  • Same data is requested repeatedly
  • Data does not change every second
  • Some temporary staleness is acceptable
  • Application wants control over what is cached

Typical examples:

  • Product catalog
  • Customer profile
  • Configuration
  • Reference data
  • Popular content
  • Frequently accessed API responses

18. Interview Scenario

Interviewer: Explain Cache-Aside.

Answer:

“In Cache-Aside, the application checks the cache first. If the data exists, it returns the cached value. If there is a cache miss, the application reads from the database, stores the result in the cache, and returns it. During an update, a common approach is to update the database and invalidate the corresponding cache key so that the next read loads the latest value.”

19. Common Interview Questions

Q1. Who manages the cache in Cache-Aside?

The application manages it. The application explicitly reads, writes, and invalidates cache entries.

Q2. What happens on a cache miss?

Cache Miss
    ↓
Database
    ↓
Store in Cache
    ↓
Return Response

Q3. What happens when data changes?

A common strategy is to update the database and invalidate the corresponding cache key.

Q4. Is Redis responsible for reading SQL Server?

No. In Cache-Aside, the application reads SQL Server after a cache miss and then puts the result into Redis.

Q5. What is the biggest challenge?

Cache invalidation and consistency between the cache and the source of truth.

Q6. What happens if Redis fails?

The application can fall back to the database, but the system must protect the database from a sudden large number of cache misses.

Q7. Cache-Aside vs Read-Through?

In Cache-Aside, the application explicitly reads the database and populates the cache. In Read-Through, the cache layer itself is responsible for loading missing data from the underlying data store.


20. Key Takeaways

  • Cache-Aside = Application manages the cache.
  • Application checks cache first.
  • Cache hit → return cached data.
  • Cache miss → read database.
  • After a miss, application populates cache.
  • Database remains the source of truth.
  • On updates, a common approach is database update followed by cache invalidation.
  • Redis is commonly used as the distributed cache.
  • Cache-Aside is simple and widely used.
  • The major challenge is cache consistency and invalidation.

Conclusion

The most important Cache-Aside flow to remember for interviews is:

flowchart TD
    A[Request] --> B[Check Cache]

    B -->|HIT| C[Return Cached Data]

    B -->|MISS| D[Read Database]
    D --> E[Store in Cache]
    E --> C

    F[Update Request] --> G[Update Database]
    G --> H[Invalidate Cache]

Key interview sentence:

“Cache-Aside is a caching pattern where the application manages the cache. It checks the cache first, reads from the database on a miss, stores the result in the cache, and commonly invalidates the cache after database updates. Redis is often used as the distributed cache.”

Comments & Ratings (0)

Log in or create a student account to comment and rate this article.

Be the first to comment on this article.

Never miss a new post 📬

Get an email whenever a new article, project, cheatsheet or video drops.

✅

ℹ️