Episode 6 — Scaling Reliability Microservices Web3 / 6.6 — Caching in Production
6.6 — Exercise Questions: Caching in Production
Practice questions for all three subtopics in Section 6.6. Mix of conceptual, coding, design, and debugging tasks.
How to use this material (instructions)
- Read lessons in order —
README.md, then6.6.a→6.6.c. - Answer closed-book first — then compare to the matching lesson.
- Try the coding exercises — spin up Redis with Docker and experiment hands-on.
- Interview prep —
6.6-Interview-Questions.md. - Quick review —
6.6-Quick-Revision.md.
6.6.a — Redis Caching (Q1–Q12)
Q1. Define Redis in one sentence. Why is it faster than a traditional database like PostgreSQL or MongoDB for read-heavy operations?
Q2. Your Express.js app runs 4 instances behind a load balancer. A developer proposes caching with a JavaScript Map() object in each instance. Name three problems this creates and explain how Redis solves each one.
Q3. List the five main Redis data types and give one caching use case for each.
Q4. Write the ioredis code to: (a) connect to Redis, (b) store a user object with a 10-minute TTL, (c) retrieve and parse it, (d) delete it.
Q5. Explain the cache-aside (lazy loading) pattern. Write pseudocode for both the read path and the write/invalidation path.
Q6. Compare cache-aside vs write-through caching. In which scenario would you prefer write-through despite its slower writes?
Q7. What is the risk of using the write-behind (write-back) pattern? Give an example of data where write-behind is acceptable and data where it is not.
Q8. Explain what cache warming is. When would you perform it? Write a code snippet that warms the cache for the top 50 featured products on application startup.
Q9. Why is redis.pipeline() faster than executing commands one by one? When should you use it?
Q10. Hands-on: Run Redis locally with Docker (docker run --name redis-dev -p 6379:6379 -d redis:7-alpine). Connect via redis-cli and execute: SET greeting "hello" EX 30, then GET greeting, then wait 30 seconds and GET greeting again. What happens?
Q11. Explain why sessions should be stored in Redis rather than in-memory or in the database in a production environment with multiple app servers.
Q12. Design exercise: You have a microservices architecture with User Service, Order Service, and Product Service. The Order Service frequently needs user data. Design a caching strategy that avoids the Order Service calling the User Service API for every request. Include cache key naming, TTL, and invalidation approach.
6.6.b — Cache Invalidation (Q13–Q24)
Q13. Complete the famous quote: "There are only two hard things in Computer Science: _____ and _____." Why is cache invalidation specifically so difficult?
Q14. Describe the race condition that can occur between updating the database and invalidating the cache. Draw a timeline showing two concurrent requests that result in stale cached data.
Q15. You update a user's name in the database and delete user:123 from the cache. But the user's old name still appears on: the team page, the leaderboard, and search results. What went wrong? How would you prevent this?
Q16. Compare three invalidation strategies: time-based (TTL), event-based (delete on write), and version-based (versioned keys). For each, give one scenario where it is the best choice.
Q17. Write the code for a version-based cache key system: store a version counter, include it in the cache key, and increment it on update.
Q18. Explain the cache stampede (thundering herd) problem. A popular product's cache key expires and 500 concurrent users request it. What happens to the database?
Q19. Write a getWithLock() function that uses Redis SET NX to prevent cache stampede by ensuring only one request repopulates the cache while others wait.
Q20. Explain the probabilistic early expiration approach to stampede prevention. Why does it work better than simple locking in some scenarios?
Q21. What is the stale-while-revalidate pattern? How does it improve user experience compared to a simple cache miss?
Q22. Bug hunt: The following code has a cache invalidation bug. Find it and explain the fix:
async function updateUserEmail(userId, newEmail) {
await redis.del(`user:${userId}`);
await db.updateOne({ _id: userId }, { $set: { email: newEmail } });
}
Q23. Your application invalidates 10,000 product cache keys during a nightly price update. The next morning, the first wave of traffic causes a massive spike in database queries. Diagnose the problem and propose two solutions.
Q24. Design exercise: Design a pub/sub-based distributed invalidation system for a microservices architecture. When the Product Service updates a product, how do the Order Service, Search Service, and Cart Service all learn to invalidate their cached product data?
6.6.c — TTL Strategies (Q25–Q37)
Q25. What does TTL stand for, and what happens when a Redis key's TTL reaches zero?
Q26. Fill in recommended TTL values for each data type: (a) user session, (b) product price during a flash sale, (c) homepage banner content, (d) real-time stock price, (e) user avatar URL.
Q27. A developer sets all cache keys to a 24-hour TTL "for simplicity." Name three problems this creates.
Q28. Explain the difference between sliding TTL and fixed TTL. Write code that implements sliding TTL (resets the TTL on every read).
Q29. What is the danger of sliding TTL without a maximum age? How would you prevent a frequently accessed key from being stale forever?
Q30. Explain the difference between these HTTP Cache-Control values: (a) public, max-age=300, (b) private, max-age=60, (c) no-cache, (d) no-store.
Q31. What is an ETag? Write Express.js middleware that generates an ETag from the response body and returns 304 Not Modified when the content hasn't changed.
Q32. What is the difference between max-age and s-maxage in Cache-Control? When would you set s-maxage to a longer value than max-age?
Q33. Explain the stale-while-revalidate Cache-Control directive. How does it differ from the application-level stale-while-revalidate pattern you might implement in Redis?
Q34. Name the four caching layers in a production web application (from client to database) and explain what each layer caches.
Q35. Calculation: Your API endpoint gets 100,000 requests per hour. The database query takes 200ms and your server costs $0.10 per million database queries. With a Redis cache (TTL = 5 minutes, 95% hit rate), how many DB queries per hour are saved? What is the hourly cost savings?
Q36. Why should inner caching layers (Redis) have a shorter or equal TTL compared to outer layers (CDN, browser)? What happens if the Redis TTL is 1 hour but the CDN TTL is 5 minutes?
Q37. Design exercise: Create a complete caching strategy for a social media application with the following endpoints: (a) GET /feed — personalized feed, (b) GET /posts/:id — individual post, (c) GET /users/:id/profile — public profile, (d) POST /posts — create a post. For each, specify: HTTP Cache-Control header, Redis TTL, invalidation trigger, and whether CDN caching is appropriate.
Answer Hints
| Q | Hint |
|---|---|
| Q2 | Inconsistency across instances, lost on restart, duplicated memory usage |
| Q6 | Write-through when reads vastly outnumber writes and you need guaranteed cache freshness (e.g., config data) |
| Q7 | Write-behind risks data loss; acceptable for analytics/counters, not for payments/orders |
| Q14 | Request A reads DB (old data), Request B updates DB + invalidates cache, Request A writes old data to cache |
| Q18 | 500 concurrent DB queries for the same data — potential database overload |
| Q22 | Cache is deleted BEFORE the DB is updated — wrong order. Another request can read stale DB data and re-cache it |
| Q23 | Cache stampede after bulk invalidation — solutions: stagger invalidation, or re-warm instead of invalidating |
| Q26 | (a) 24-48h, (b) 30-60s, (c) 15-60m, (d) 5-15s, (e) 5-30m |
| Q27 | Stale prices for 24h, wasted memory on rarely accessed data, no dynamic adaptation to data volatility |
| Q30 | (a) CDN+browser cache 5min, (b) browser only 1min, (c) cache but always revalidate, (d) never cache |
| Q32 | s-maxage controls CDN TTL separately from browser. Longer s-maxage = CDN serves cached while browser revalidates more frequently |
| Q35 | 95,000 queries saved/hour. 100K * 0.05 = 5,000 DB queries instead of 100K. Savings: 95,000 * $0.10/1M = $0.0095/hour |
| Q36 | If CDN TTL < Redis TTL, CDN revalidates with the app server, but the app server serves stale Redis data — defeating the CDN refresh |
← Back to 6.6 — Caching in Production (README)