Command Palette

Search for a command to run...

Hectal
PHASE 6Intermediate ~10 min· topic 1 of 7

Topic 6.1

Cache-Aside: The Default Pattern

In one line

In cache-aside (lazy loading), the application checks Redis first, and on a miss reads the database and stores the result with a TTL. Writes go to the database and then delete the cached entry. It's simple, resilient to cache failure, and the default choice, as long as you handle its races.

0/7 · 0%

Think of it like this

A chef's prep station. When an order comes in, the chef checks the station first. If the chopped onions are there, great. If not, they go to the walk-in fridge, chop some, use them, and leave the extra on the station for next time. The station is never the source of truth; the fridge is.

Key ideas

  1. 01

    Read path: GET product:9 → hit: return. Miss: query the database, SET product:9 <value> EX <ttl>, return. The application owns all the logic; Redis is just storage.

  2. 02

    Write path: update the database, then DEL product:9 (invalidate), rather than writing the new value to the cache. Deleting is idempotent and avoids racing writers leaving the older value in the cache. The next read repopulates it.

  3. 03

    TTL is your safety net: even if an invalidation is lost (crash between the database write and the DEL, network error), the stale value disappears after the TTL. Choose the TTL from how long stale data is acceptable, and add jitter.

  4. 04

    Resilience: if Redis is down, reads fall back to the database (slower, and possibly overloading it, so protect the database with timeouts, circuit breakers and rate limits). The cache is an optimisation, not a dependency for correctness.

  5. 05

    Cache only what's worth it: data read far more than written, expensive to compute, and tolerant of brief staleness. Don't cache everything; measure hit ratio per keyspace.

  6. 06

    Serialise deliberately: versioned keys (product:v2:9) and a stable format (Topic 8.3) so deployments and rollbacks don't break cached values.

Code & diagrams

cache-aside.mermaiddiagram
Rendering diagram…
ProductService.javajava
@Service
public class ProductService {
    private static final Duration TTL = Duration.ofMinutes(10);
    private final StringRedisTemplate redis;
    private final ProductRepository repo;
    private final ObjectMapper json;

    public Product get(long id) throws IOException {
        String key = Keys.product(id);
        String cached = redis.opsForValue().get(key);
        if (cached != null) return json.readValue(cached, Product.class);   // hit

        Product p = repo.findById(id).orElseThrow();                         // miss
        Duration ttl = TTL.plusSeconds(ThreadLocalRandom.current().nextInt(0, 120)); // jitter
        redis.opsForValue().set(key, json.writeValueAsString(p), ttl);
        return p;
    }

    @Transactional
    public void updatePrice(long id, BigDecimal price) {
        repo.updatePrice(id, price);
        // Delete AFTER commit, so a reader can't re-cache the old row in between.
        TransactionSynchronizationManager.registerSynchronization(new TransactionSynchronization() {
            @Override public void afterCommit() { redis.delete(Keys.product(id)); }
        });
    }
}

Interview problem

The problem

Implement cache-aside for product pages

Product pages read product details from PostgreSQL at 20K reads/sec. Add a Redis cache-aside layer. Explain hits, misses, TTL, invalidation on update, and what happens when Redis is unavailable.

You're given

  • 20K reads/sec
  • 200 writes/sec
  • Price changes must show within 1 minute
  • Database max ~3K queries/sec

The interviewer follows up

01

Why delete on write instead of updating the cache with the new value?

When it breaks

Redis outage with no database protection

What you see

Every request becomes a database query; the database saturates, latency explodes for all features, and the outage spreads beyond caching.

Fix & prevent

Short Redis timeouts, circuit breakers, an in-process L1 cache, request coalescing, and load shedding; capacity-plan the database for partial cache loss.

Explain it without notes

01

Walk through cache-aside reads and writes and explain why the TTL is still needed.

Practice

01

Add cache-aside to an endpoint in your own project and measure hit ratio and p95 latency before and after.

Trade-offs

  • ↔

    Cache-aside is simple and fault-tolerant, but the first read after a miss is slow and races must be handled.

  • ↔

    Short TTLs reduce staleness and increase database load; long TTLs do the opposite.

Done when you can

  • I can implement cache-aside with TTL, jitter and delete-after-commit.

  • I can explain why delete beats update on writes.

  • I plan for Redis outages with timeouts and database protection.