Art of Codeartofcode
LLeeaarrnniinngg
BBlloogg
TToooollss
GGaammeess AArreennaa
JJoobbss
LLiivvee CCoouurrsseess

Courses

  • System DesignHigh-level system design, one everyday scene at a time: 30 concepts and 15 interview rounds.712 lessons
All coursesTutorialsMy Learning

Categories

  • 01AI & Tools
  • 02Tutorials
  • 03Engineering
  • 04Reviews
  • 05Build Logs
  • 06Tales of Misfits
  • 07Opinion
  • 08Career
  • 09News

Read & write

All storiesEvery post, newest first.Write for usPublish your stories and earn from what you write.Writer leaderboardsWho is writing the most read posts.

Categories

  • PDF Tools25
  • Development09
  • Utilities10
  • Design04
  • Converters04
  • Generators02

Featured

JSON FormatterFormat, validate and minify JSON.Regex TesterTest patterns against live text.Merge PDFCombine PDFs in your browser.JWT DecoderRead a token’s header and claims.Password GeneratorStrong passwords, generated locally.Gradient GeneratorBuild CSS gradients visually.
All 53 tools

Play a round

DevdleOne developer word a day, six guesses.Python RushClassic snake, with coding icons to eat.Blink or BreakA quick test of your reflexes.
Games Arena

Learn with us

Live CoursesInstructor-led courses. Join a waitlist and be first in when one starts.LearningStructured programming courses with AI assistance and certificates.

System Design

12. Caching: The Basics

Lesson 14 of 16

12. Caching: The Basics

1
The picture to keep in your head
2
12.1 What is a cache, and what are a hit, a miss and the hit rate?
3
12.2 Why is a cache faster than the database?
4
12.3 Where does a cache physically live?
5
12.4 On a miss, who fetches the data?
6
12.5 When the data changes, what gets written where?
7
12.6 What happens when the real data changes, and why add jitter?
8
12.7 What happens when the cache is full?
9
12.8 How do you invalidate safely, and what's the race everyone hits?
10
12.9 When should you cache, what should you never cache, and how do you name keys?
11
Memory card and where the analogy breaks
12
Practice: five levels
13
Interview questions
14
Solutions
15
Sources
16
Code lab
12. Caching: The Basics

Solutions

3 min read+10 XP

Solutions

Level 1

reveal

Level 2

reveal

Level 3

reveal

Level 4

reveal

Level 5 (sketch)

reveal

No tracks available

Lofi Player

artofcode

artofcode.in

The den of devs.

Stories, tools, games, and gear for developers.

Read the StoriesEExxpplloorree TToooollss
Art of Codeartofcode

Platform

  • BBlloogg
  • TToooollss
  • GGaammeess AArreennaa
  • CCoouurrsseess
  • LLeeaarrnniinngg
  • CCaammppuuss

Company

  • AAbboouutt UUss
  • JJoobbss && IInntteerrnnsshhiippss
  • CCoonnttaacctt

Legal

  • TTeerrmmss ooff SSeerrvviiccee
  • PPrriivvaaccyy PPoolliiccyy
  • RReeffuunndd PPoolliiccyy

Copyright © 2026 artofcode.in, All Rights Reserved.

IST·--:--

1Show the Level 1 solution
21. **Countries list: cache it, long TTL (hours to a day).** Repeated, rarely changes, the same for everyone. It can even live in the browser or a CDN.
32. **Bank balance at payment: don't cache.** Urgent truth. Read it from the source of record at that moment.
43. **Trending list: cache it, short TTL (around 30 to 60 seconds).** Read by everyone, expensive to compute, and being slightly behind is fine.
54. **Random search text: usually don't cache the results.** Unique per request, so the hit rate would be near zero. (Caching the most popular searches can still help.)
65. **Profile photo URL: cache it, long TTL plus delete when the user changes their photo.** Read constantly, rarely changes.
76. **Likes count: write-back.** Count increments in the cache and flush totals to the database every few seconds. Thousands of writes collapse into a few, and losing a few seconds of likes in a crash is acceptable. (Never for money.)
Show the Level 2 solution
1. Average = 0.8 × 2 ms + 0.2 × 40 ms = 1.6 + 8 = **9.6 ms**.
2. Misses = 20% of 1,000 = **200 queries per second** to the database.
3. To stay at or under 100 queries per second, misses can be at most 100 ÷ 1,000 = 10%, so you need a hit rate of **at least 90%**.
4. All 10,000 keys **expire in the same second**, so 10,000 requests miss together and hit the database at once (an avalanche, Chapter 13). Use a TTL of **300 ± 30 seconds** (270 to 330) to spread them across about a minute; in the chapter's code, the busiest second drops from 10,000 expiries to about 200.
1Show the Level 3 solution
2Tested solution (Python 3, with a fake clock so tests don't sleep):
3
4```python
5from collections import OrderedDict
6
7
8class TTLCache:
9    """A small in-memory cache with a size limit (LRU eviction) and expiry (TTL)."""
10
11    def __init__(self, capacity, ttl_seconds, clock):
12        self.capacity, self.ttl, self.clock = capacity, ttl_seconds, clock
13        self.items = OrderedDict()           # key -> (value, expires_at), oldest-used first
14        self.hits = self.misses = 0
15
16    def get(self, key):
17        entry = self.items.get(key)
18        if entry is None or entry[1] <= self.clock():
19            self.items.pop(key, None)        # expired counts as a miss
20            self.misses += 1
21            return None
22        self.items.move_to_end(key)          # mark as most recently used
23        self.hits += 1
24        return entry[0]
25
26    def set(self, key, value, ttl=None):
27        self.items[key] = (value, self.clock() + (self.ttl if ttl is None else ttl))
28        self.items.move_to_end(key)
29        if len(self.items) > self.capacity:
30            self.items.popitem(last=False)   # evict the least recently used
31
32    def delete(self, key):
33        self.items.pop(key, None)
34
35    def hit_rate(self):
36        total = self.hits + self.misses
37        return self.hits / total if total else 0.0
38
39
40class WriteBack:
41    def __init__(self, cache, db):
42        self.cache, self.db, self.dirty = cache, db, set()
43
44    def write(self, key, value):
45        self.cache.set(key, value)
46        self.dirty.add(key)                  # remember what the database hasn't seen
47
48    def flush(self):
49        for k in sorted(self.dirty):
50            self.db.set(k, self.cache.items[k][0])
51        self.dirty.clear()
52```
53
54All four checks pass, plus the extension: the write-back wrapper writes nothing to the database until `flush()`, and a crash before flushing loses the unflushed price change; with ±30 s of jitter, 10,000 keys' busiest expiry second drops from 10,000 to 208. The full file (also with LFU, write-through, write-around and the Level 4 race) is in `code/ch12_caching.py`.
1Show the Level 4 solution
2**The bug:** the cache is deleted **before** the database is updated.
3
41. Admin calls `update_price("milk", 56)`. The cache key is deleted.
52. The database update starts. It takes about 200 ms.
63. During those 200 ms, a user requests milk. Cache miss.
74. That request reads the database, which **still says ₹52**, because the update hasn't finished.
85. It stores ₹52 in the cache with a **10-minute TTL**.
96. The database update finishes (₹56), but the cache now holds ₹52 for up to 10 minutes.
10
11**The fix:** update the database first, then delete the cache.
12
13```python
14def update_price(item, price):
15    db.update_price(item, price)
16    cache.delete(f"price:{item}")
17```
18
19Also consider a shorter TTL for prices, so any rare leftover staleness heals quickly. (The chapter's code simulates both orders: delete-first leaves ₹52 cached; update-first leaves ₹56.)
1Show the Level 5 (sketch) solution
21. **Cache:** product name, images, description, and display prices. **Never cache:** the price used at checkout, payment status, and the final stock check.
32. **Layers:** CDN for images and static assets; Redis for product data and display prices; optionally a tiny in-app cache for the 10 hottest products to take load off Redis.
43. **TTLs:** product details, hours plus delete on edit. Display prices, 30 to 60 seconds plus delete whenever a flash discount starts. Stock, a very short TTL for an approximate "few left" label only. Add jitter to all of them.
54. **Checkout:** read the price from the database at checkout and compare it with what the user saw. If it changed, show the new price before payment.
65. **If the cache goes down:** the database gets everything at once. Protect it with timeouts on cache calls, limits on database connections and queries, serving the CDN-cached page where possible, and warming the cache with the hot products before reopening traffic (Chapter 13).