Design Twitter (News Feed)
The most important aspect is designing the news feed generation at scale. This involves the classic fan-out trade-off.
1. Requirements
- · Post tweets (text, images, videos)
- · Follow / unfollow users
- · View home timeline (chronological feed)
- · View user profile timeline
- · Like and retweet
- · Search tweets
- · 300M monthly active users
- · 500M tweets posted per day
- · Average user follows 200 accounts
- · Average 100 followers per user
- · Home timeline reads: 300M/day
- · Read:Write = ~100:1
2. Estimation
3. The Core Design Challenge: Fan-out
When a user tweets, immediately push the tweet ID to all followers' feed caches (Redis sorted sets).
When a user opens their feed, fetch latest tweets from all followed users and merge-sort them.
Regular users (<10K followers): Fan-out on write. Tweet is pushed to followers' feed caches at write time.
Celebrity users (>1M followers, e.g. verified): Fan-out on read. Their tweets are fetched and injected at read time.
At feed load time: Merge precomputed feed (regular users) + live fetch celebrities' recent tweets → sort by timestamp.
4. Architecture
5. Database Choices & Rationale
| Data | Storage | Why |
|---|---|---|
| Users, follows | MySQL | Relational data. Needs JOIN (user → follow → user). ACID transactions. |
| Tweets | Cassandra | High write QPS (5,800/sec). Partition by user_id. Time-series by tweet_id (Snowflake ID encodes time). |
| Feed cache | Redis Sorted Set | O(log n) inserts/reads. Score = tweet timestamp. Range queries for pagination. |
| Tweet search | Elasticsearch | Full-text search. Inverted index on tweet content. Near real-time indexing. |
| Media | S3 + CDN | Object storage for photos/videos. CDN edge caches for read performance. |
| Trending topics | Redis Counter | Increment counter per hashtag per time window. Top-K using sorted set. |
| Notifications | Cassandra | Append-heavy, time-series. Query by user_id + timestamp for notification feed. |
6. Bottlenecks & Solutions
Identify celebrities at write time (follower count > threshold). Skip fan-out for them. Inject their tweets at read time. Redis pipeline for batch writes for regular users.
On user login, async warm their cache with latest 100 tweets. Background job pre-populates caches for users who log in regularly (predicted by ML).
Cassandra partition key = (user_id + date). Date bucketing distributes load across nodes over time. Add read replicas for popular user_ids.
Store only tweet_ids in Redis sorted set (8 bytes each). Full tweet content fetched from Tweet Cache/DB. Cap feed size at 1000 entries per user; evict oldest.
7. Key Trade-offs Summary
| Decision | Option A | Option B | Chosen & Why |
|---|---|---|---|
| Fan-out strategy | Push (write-time) | Pull (read-time) | Hybrid: push for regular, pull for celebrities |
| Tweet storage | MySQL | Cassandra | Cassandra — handles 5.8K write QPS, scales horizontally |
| Feed storage | DB table | Redis sorted set | Redis — O(log n) operations, sub-ms reads |
| Feed delivery | Polling | WebSocket/SSE | Long polling or WebSocket for real-time updates |
| Consistency | Strong (SQL) | Eventual (NoSQL) | Eventual — users tolerate 100ms stale timeline |
| Media | App server | S3 + CDN | S3 + CDN — infinite scale, fast edge delivery |
8. Follow-up Questions
A: Sliding window counter per hashtag. Every tweet increments hashTag counter for current time window (e.g. 15-minute buckets). Aggregate buckets over past hour. Top-K via Redis sorted set. Decay older windows to give recency bias.
A: Soft delete: mark tweet as deleted in DB. Lazy propagation: remove from Redis caches on next access. Background job reconciles deleted tweets from feed caches. Fan-out deletion event via Kafka.
A: Real-time indexing: as tweet is created, push to Elasticsearch via Kafka consumer. Elasticsearch inverted index on tweet text. Sharding by tweet_id range. Search ranking: recency + engagement score (likes + retweets). Cache popular queries in Redis.
A: Fan-out on read for this user (too many accounts to merge efficiently from cache). Build their feed on read by querying recent tweets from each followed account. Pagination helps: only need latest 200 tweets. Precompute during off-peak hours.