Topic 10.2
Cache-aside (Lazy Loading)
In one line
The app checks the cache, loads from the DB on miss, and writes the cache itself — the default strategy.
Think of it like this
Checking your fridge first before going to the store. If the milk's there (cache hit), great, done. If not (cache miss), you go to the store (database), buy it, AND put a copy in the fridge for next time.
Key ideas
- 01
Read path: get(key) → hit? done : DB load → put cache → return.
- 02
Advantages: cache only holds what's actually read; simple; cache holds app-specific shapes.
- 03
Risk #1: cache stampede on miss (see the blog post on the subject) → single-flight + TTL jitter.
- 04
Risk #2: stale cache on write → evict on write (@CacheEvict) or TTL-bounded staleness.
- 05
Write path rule: invalidate on write (delete key), DON'T write-through on every write — one less race.
- 06
Interview: 'cache-aside with eviction on write and single-flight on miss' is a complete sentence.
Java / Spring map
- →
The full LoadingCache single-flight code lives in phase-5 → concurrent-cache.
Code & diagrams
The read/write path with eviction — the canonical pair.
@Service
public class ProfileService {
private final ProfileRepository db;
private final StringRedisTemplate redis; // cache-aside on Redis
private static final String KEY = "profile:";
public Profile get(String userId) {
String json = redis.opsForValue().get(KEY + userId); // 1. check cache
if (json != null) return Profile.fromJson(json); // hit → done
Profile p = db.findById(userId).orElseThrow(); // 2. DB miss path
redis.opsForValue().set(KEY + userId, p.toJson(), Duration.ofMinutes(15)); // 3. populate
return p;
}
@CacheEvict(value = "profiles", key = "#userId") // 4. evict on write
public Profile update(String userId, Profile newData) {
return db.save(newData);
}
}Explain without notes
Show the race: two readers miss, both load, both write — why is that mostly harmless here, and when is it NOT?
Practice
Add TTL jitter and single-flight to the get() and justify each in a comment.
Trade-offs
- ↔
App-managed because the app owns the shapes; teams must not forget the eviction half.
Run it in production
You've designed it. Now build, operate, and break the same idea hands-on in the DevOps courses:
Completion checklist
I treat cache-aside as: check, miss, load, populate, and EVICT ON WRITE.