01Rate limiting02Databases03Caching04Flash sale05Idempotency06Circuit breaker07Queues & DLQ

Lab / 03 · Caching

Same request twice — see the latency drop and X-Cache flip to HIT.

A cache-aside pattern checks Redis before touching the database. The first request takes 70+ms to query PostgreSQL; subsequent requests hit in-memory Redis in <2ms with an instant X-Cache: HIT.

LIVE · TTL 30s · Redis Cache-Aside
Hits: 0Misses: 0Hit Rate: 0%
Active Key Status:Not fetched yet
Redis TTL Remaining:0s / 30s
// Network log & response headers
Click "Fetch item" or "Burst 5 requests" to inspect real-time network traffic and X-Cache headers.

k6 Benchmark: Direct PostgreSQL vs Redis Cache-Aside

PostgreSQL p99 Latency
138.6 ms
Redis Cache p99 Latency
2.8 ms
Latency Reduction
98.5%
Database Load Reduction
98.4%

On the live site, these requests hit real Spring Boot endpoints querying Redis with atomic TTLs and PostgreSQL fallback.

The cache-aside architecture

Read requests check in-memory Redis first. On a cache miss, data is queried from PostgreSQL and written back to Redis with an atomic TTL.

Client
browser or curl
Spring Boot
CacheLabService
Redis
In-memory, TTL 30s
PostgreSQL
Cold storage / DB
Cache Hit: ~1-2ms | Cache Miss: ~75ms (queries DB + populates cache)

Cache-Aside Implementation

public CacheItemResult getOrCompute(String key) { String redisKey = "lab:cache:" + key; // 1. Check Redis memory cache String cached = redisTemplate.opsForValue().get(redisKey); if (cached != null) { long ttl = redisTemplate.getExpire(redisKey); return new CacheItemResult(key, parse(cached), true, 1L, ttl, "Redis"); } // 2. Cache Miss: Query database (~75ms) Map<String, Object> data = databaseRepository.findById(key); // 3. Populate Redis with atomic TTL (30s) redisTemplate.opsForValue().set(redisKey, toJson(data), Duration.ofSeconds(30)); return new CacheItemResult(key, data, false, 75L, 30L, "PostgreSQL"); }
Full source on GitHub →

Trade-offs I weighed

Cache-Aside vs Read-Through

Cache-aside keeps application logic in control of fallback behavior, retry logic, and fallback serialization if Redis is temporarily unreachable.

TTL vs Active Invalidation

A 30s TTL guarantees data eventually becomes consistent even if an invalidation message is dropped during network partitions.

Thundering Herd / Cache Stampede

When high-frequency keys expire, hundreds of concurrent threads can hit the DB simultaneously. Mutex locking or probabilistic early recomputation (XFetch) protects hot keys.

← ← 02 DatabasesNext: 04 Flash sale → →