All quizzesHard
Architecture Case Studies
Preview — 3 of 10 questions
You're designing a URL shortener (like Bit.ly) handling 100M daily active users. 10M new URLs are shortened per day; 500M redirects per day. What is the primary architectural concern and how do you address it?
javascript
Scale analysis:
Writes: 10M/day = 115 writes/sec (very manageable)
Reads: 500M/day = 5,800 reads/sec (6,000 redirects every second!)
Ratio: 50:1 reads-to-writes → heavily read-optimized
Hot path: the redirect (must be < 10ms)
GET /abc123 → 301 https://your-long-url.com/...
This is called billions of times. Every millisecond matters.
Architecture:
Short URL format: 7 characters [a-z, A-Z, 0-9] = 62^7 = 3.5 trillion combos
Redirect path:
1. Read short code from URL path (/abc123)
2. Redis.get("url:abc123") → cache hit? Return 301. Done!
3. Cache miss → PostgreSQL lookup → cache result → return 301
Write path:
1. Receive long URL
2. Generate unique short code:
Option A: Base62 encode auto-increment DB ID (simple, no collision)
Option B: Murmur hash of URL + truncate to 7 chars (need collision check)
Option C: Pre-generate codes in bulk, pop from a pool table
3. INSERT INTO urls (short_code, long_url, user_id, created_at)
4. Return short URL
Cache sizing:
Top 20% of URLs serve 80% of traffic
500M × 20% × (7 + 100 bytes) = ~10 GB Redis cache
→ Single Redis node handles all hot URL lookupsAWrite performance — the system creates 10M new URLs per day, which requires sharded writes across many databases.
BThe URL shortener must use a graph database to efficiently traverse URL redirect chains.
CGenerating unique short codes requires a distributed consensus algorithm (Raft) to prevent duplicates.
DRead performance with a 50:1 read-to-write ratio — the system is heavily read-optimized. The redirect path must be as fast as possible: a Redis cache keyed by short code returns the long URL in microseconds, avoiding DB lookups for hot URLs.
Two users submit the same URL to be shortened concurrently. Should they get the same short code or different ones? What data structure prevents duplicate short codes?
javascript
Two valid approaches — with different tradeoffs:
Option 1: Always create unique short codes
Pros:
- Simple, no deduplication logic
- Multiple tracking links for same destination (A/B testing)
- No hash collisions to handle
Cons:
- Same URL has many short codes → wasted storage
- User can't reuse their link for the same destination
Option 2: Deduplicate by URL (Bit.ly's approach for logged-in users)
Implementation:
CREATE UNIQUE INDEX ON urls(user_id, long_url_hash);
-- long_url_hash = SHA-256 of long URL (for fixed-length indexing)
On conflict:
INSERT INTO urls (short_code, long_url_hash, user_id) VALUES (...)
ON CONFLICT (user_id, long_url_hash) DO UPDATE SET updated_at = NOW()
RETURNING short_code;
→ Atomic upsert — no race condition, no distributed lock needed
Short code generation — the right approach:
Auto-increment ID → Base62 encode:
ID: 1 → "000001"
ID: 125 → "000021" (5 × 25 + 0)
ID: 1,000,000 → "4c92"
Advantages:
- No collisions by definition (IDs are unique)
- No need to check if code already exists
- Deterministic, debuggable
Distributed ID generation (for multi-server):
- Snowflake ID: 64-bit = 41-bit timestamp + 10-bit machine ID + 12-bit sequence
- Each server generates unique IDs without coordinationADeduplicate by URL: hash the long URL to detect existing entries, returning the existing short code. A unique index on long_url in the database prevents race conditions on concurrent inserts.
BAlways generate a new short code per request — two users can get different short codes for the same URL.
CUse a distributed lock (Redis SETNX) around every insert to serialize all URL creations globally.
DUse a UUID as the short code — UUIDs are globally unique and never collide.
Twitter has celebrities with 100M followers. When a celebrity tweets, how should their tweet reach all followers' feeds?
javascript
Fan-out on Write (push model):
Celebrity tweets:
→ Worker reads 100M follower IDs
→ Writes tweet_id to each follower's Redis feed list: LPUSH feed:user_123 tweet_id
→ Each user's feed: pre-computed, read is O(1) cache lookup
Problem: 100M followers × write latency → tweet delivery takes hours!
Worse: Lady Gaga + BTS both tweet simultaneously → millions of writes at once
Fan-out on Read (pull model):
User requests feed:
→ Fetch IDs of everyone they follow
→ SELECT * FROM tweets WHERE author_id IN (follow_ids) ORDER BY time LIMIT 20
Problem: User follows 500 people → 500 DB queries per feed load → slow!
Worse: celebrity's 100M followers all polling → same tweet fetched 100M times
Twitter's actual solution (hybrid):
Regular users (< 1M followers): fan-out on write
Fast reads: feed is pre-built in Redis
Acceptable write load: 1M writes is manageable
Celebrities (> 1M followers): fan-out on read
Their tweets are NOT pre-pushed to feed caches
At read time: your pre-built feed + celebrity tweets fetched separately
Cached at tweet level: tweet_data:tweet_id → same data reused for all 100M readers
Feed assembly at read time:
pre_built_feed = Redis.lrange("feed:#{user_id}", 0, 20) # non-celebrity follows
celeb_tweets = fetch_celeb_tweets(user's celebrity follows, limit=20)
merged_feed = merge_and_sort(pre_built_feed, celeb_tweets, limit=20)AFan-out on write: when the tweet is created, immediately write it to all 100M followers' feed caches. All reads are then simple cache lookups.
BFan-out on read: don't pre-populate feeds. When a user requests their feed, fetch the last 20 tweets from each person they follow in real time (JOIN query). Fast writes, slow reads.
CHybrid: fan-out on write for normal users (< 1M followers); fan-out on read for "celebrities" (> 1M followers). Both groups' feeds are merged at read time.
DWrite tweets to a Kafka topic and let each follower's service consume at its own pace.
Sign up free to play
Answer all 10 questions (7 more), see explanations for every answer, and track your score.