shreyansh

HLD Playground · Case 01 · Starter · 12 min

URL Shortener

TinyURL: shorten long URLs to 7-char keys, redirect in <50ms p99. Core trick — pre-generated keys (KGS) + base62, shard by hash of short key, cache the hot 20%.

Write path → read path

Follow the packet

Solid arrows are sync (you wait for them). Dashed arrows are async fire-and-forget (click events). Press Step — predict each hop before you advance.

1 · Shorten entersPOST /shorten hits the LB (TLS + throttle) and routes to a stateless API host. Idempotency-Key m…
1/6

1 · Shorten enters

POST /shorten hits the LB (TLS + throttle) and routes to a stateless API host. Idempotency-Key means retries mint no duplicate.

Redirect semantics
Every click hits origin. Enables counting + expiry.
Cache outcome
Hot 20% of keys serve ~80% of redirects.
Write QPS avg / peak
39 / 193
Read QPS avg / peak
386 / 1.9k
Storage (mappings)
~6.0 TB
+ replicas/indexes ≈ 3× raw
Origin QPS after CDN+cache
~386
302 tracked: every click hits origin
Cache hit ratio
95%
p99 < 10ms on hits
Keyspace (7-char base62)
0.3% used
62⁷ ≈ 3.5T keys — lasts decades

Test yourself

1. Why KGS instead of DB auto-increment?

Hosts mint from an in-memory range; the DB only hands out ranges. Single-writer bottleneck disappears.

2. When is 301 correct?

301 is cacheable forever. Anything with TTL/analytics must be 302 so origin enforces and counts.

3. How are DB shards chosen?

Lookups are always point queries, so hash sharding spreads perfectly with no range-scan cost.

4. Why no per-click MySQL write?

Redirect path does a bounded fire-and-forget send; batch jobs update MySQL hourly.
Drag nodes · scroll page normally · add ?elk to URL for auto-layout

Numbers that matter

Capacity is inside the flow above

Drag the sliders in the playground: 100M URLs/month ≈ 40 writes/s avg (200 peak), 10:1 reads, 500B records × 10 years ≈ 6TB. Flip 301 ↔ 302 and watch origin QPS swing — that one toggle is the classic interview follow-up.

Deep dives

Key generation: counter vs hash vs KGS

ApproachProsConsUse when
Counter → base62Simple, shortestSPOF, leaks orderPrototype
Hash(longURL)Stateless, free dedupCollision loop, grindingDedup workloads
KGS rangesNo hot-path coordinationTiny HA serviceProduction

Cache: Redis LRU + 60s negative-cache for 404s, singleflight on miss, TTL jitter against stampedes. Analytics: redirect emits to Kafka, ClickHouse serves stats — MySQL counters update hourly, never per click. First bottleneck at 10×: single Redis hot-key skew → Cluster + client-side Caffeine in front.

Say it in the room

Interview one-liners

  • Q: Why not auto-increment? / A: Single-writer bottleneck; KGS = one DB op per 10k keys.
  • Q: 301 or 302? / A: 301 only for permanent untracked; TTL/tracked is 302.
  • Q: How do you shard? / A: Hash of short_key — point lookups, perfect spread.

Full write-up: URL Shortener case note · Theory: Partitioning, Cache Patterns, Single Leader Replication · Next: Rate Limiter playground →