Chapter 48: Design Twitter/X
Case Study: Design a social media platform like Twitter/X that allows users to post short messages (tweets), follow other users, and view timelines of tweets from people they follow. The system must handle hundreds of millions of users, support real-time trending topics, and provide fast timeline generation.
Key Challenge: The fanout problem - when a celebrity with 50 million followers posts a tweet, how do you efficiently deliver it to all their followers' timelines without overwhelming the system?
1. Requirements
Functional Requirements
- Post Tweet: Users can publish tweets (280 characters max) with optional media (images, videos)
- Timeline: Users see a home timeline of tweets from people they follow, ordered by time
- Follow/Unfollow: Users can follow and unfollow other users
- Search: Users can search for tweets by keywords
- Trending Topics: Display trending hashtags and topics based on geographic location and global trends
- Notifications: Users receive notifications for mentions, retweets, likes, and new followers
- User Profile: Display user's tweets, follower count, following count
Non-Functional Requirements
- Scale: 300 million daily active users (DAU)
- Write Load: 600 tweets per second (average), 6,000 tweets/sec peak
- Read Load: 600,000 timeline reads per second
- Latency: Timeline generation under 200ms at p99
- Availability: 99.9% uptime (eventual consistency acceptable for timelines)
- Durability: Tweets must never be lost once acknowledged
Out of Scope
- Direct messaging (covered separately)
- Ads and monetization
- Tweet editing and deletion (can add later)
- Advanced recommendation algorithms (focus on chronological timeline)
2. Capacity Estimation
Traffic Estimates
Daily Active Users: 300M DAU
Tweets per day: 300M users × 2 tweets/day = 600M tweets/day
Tweets per second (average): 600M / 86,400 = 7,000 tweets/sec
Tweets per second (peak): 7,000 × 3 = 21,000 tweets/sec
Timeline reads per day: 300M users × 20 timeline views/day = 6B timeline reads/day
Timeline reads per second (average): 6B / 86,400 = 70,000 reads/sec
Timeline reads per second (peak): 70,000 × 3 = 210,000 reads/sec
Read-to-write ratio: 70,000 / 7,000 = 10:1 (actually much higher in practice, closer to 100:1)
Storage Estimates
Tweet size:
- Tweet text: 280 chars × 2 bytes (UTF-8) = 560 bytes
- Metadata (user_id, timestamp, tweet_id, etc.): 200 bytes
- Total per tweet: ~800 bytes ≈ 1 KB
Daily storage: 600M tweets × 1 KB = 600 GB/day
5-year storage: 600 GB × 365 × 5 = 1.1 PB
Media storage:
- 20% of tweets have images (avg 200 KB each)
- 5% of tweets have videos (avg 2 MB each)
- Daily media: (600M × 0.2 × 200KB) + (600M × 0.05 × 2MB) = 24TB + 60TB = 84TB/day
- 5-year media storage: 84TB × 365 × 5 = 153 PB
Fanout Calculation (Critical)
Average user has 200 followers
Fanout work per tweet: 1 tweet × 200 followers = 200 timeline writes
Total fanout writes/sec: 7,000 tweets/sec × 200 followers = 1.4M timeline writes/sec
Celebrity problem: User with 50M followers posts → 50M timeline writes for a single tweet
At 10,000 writes/sec per server, this single tweet requires 5,000 servers for several seconds - NOT FEASIBLE
Cache Estimates
Timeline cache: Store 1,000 recent tweets per user's timeline
- 300M users × 1,000 tweets × 8 bytes (tweet_id) = 2.4 TB
- Use Redis sorted sets (score = timestamp)
- Only cache active users (80/20 rule): 2.4TB × 0.2 = 480 GB
3. High-Level Architecture
Kafka] FW[Fanout Workers
Worker Pool] end subgraph "Storage" TDB[(Tweet DB
Cassandra)] UDB[(User/Graph DB
PostgreSQL)] TC[Timeline Cache
Redis] MC[Media Storage
S3/CDN] end subgraph "Search & Analytics" ES[Elasticsearch
Inverted Index] TQ[Trending Queue
Kafka] TAgg[Trending Aggregator
Stream Processing] end Client -->|Posts/Reads| LB LB --> API API --> TS API --> TLS API --> US API --> SS API --> NS TS -->|Write tweet| TDB TS -->|Upload media| MC TS -->|Publish| FQ TS -->|Index| ES TS -->|Track| TQ FQ --> FW FW -->|Write timelines| TC FW -->|Notify| NS TLS -->|Read cache| TC TLS -->|Read tweets| TDB TLS -->|Merge celebrity| TS US --> UDB SS --> ES TQ --> TAgg TAgg --> TS2
Component Responsibilities
- Tweet Service: Handles tweet creation, validation, storage, and retrieval
- Timeline Service: Generates and serves user timelines by reading from cache and merging celebrity tweets
- User Service: Manages user profiles, follow/unfollow relationships (social graph)
- Fanout Workers: Consume new tweets from queue and write to followers' timeline caches
- Search Service: Full-text search on tweets using inverted index
- Trending Service: Aggregates hashtag counts in sliding windows to detect trending topics
- Notification Service: Delivers push notifications for mentions, likes, retweets
4. Tweet Ingestion
Write Path
Tweet Validation
- Length: 280 characters max (count emojis correctly, 2-4 bytes each)
- Rate limiting: Max 2,400 tweets per day per user (prevents spam)
- Content filtering: Block spam keywords, NSFW content (async review)
- URL validation: Ensure URLs are valid and not malicious
Media Upload
- Upload flow: Client uploads media directly to S3 via pre-signed URL
- Image processing: Generate thumbnails (150x150, 400x400), compress images
- Video processing: Transcode to multiple bitrates (HLS), generate thumbnail
- CDN: Serve media from CloudFront for low-latency global access
- Storage class: S3 Standard for recent media, S3 Glacier for media older than 1 year
URL Shortening
Twitter shortens all URLs to save character count and track clicks:
- User posts: "Check out https://www.example.com/very/long/article"
- System shortens to: "Check out https://t.co/abc123"
- Shortener service maps abc123 → original URL
- Track click analytics (clicks, referrers, geographic data)
Tweet ID Generation
Snowflake ID: Twitter's custom 64-bit ID format
- 41 bits: Timestamp (milliseconds since epoch) - sortable by time
- 10 bits: Machine ID (1024 machines)
- 12 bits: Sequence number (4096 IDs per millisecond per machine)
- Total: 4M IDs per second across all machines
- Benefits: No coordination needed, sortable, unique
5. Fanout Strategies
The Fanout Problem
When a user posts a tweet, that tweet must appear in the timelines of all their followers. This process of "fanning out" a single tweet to many timelines is the core challenge of Twitter's architecture.
Option 1: Fanout-on-Write (Push Model)
Strategy: When a user posts, immediately write that tweet to all followers' timeline caches.
Timeline Cache] T2[Follower 2
Timeline Cache] T3[Follower N
Timeline Cache] User -->|Publish| Q Q --> W1 Q --> W2 Q --> W3 W1 -->|Write tweetId| T1 W2 -->|Write tweetId| T2 W3 -->|Write tweetId| T3
Fanout-on-Write Process
- User posts tweet → Tweet Service writes to database
- Tweet Service publishes message to Kafka: {tweetId, userId}
- Fanout workers consume message and query User Service for follower list
- For each follower, worker writes tweetId to that follower's Redis timeline cache
- Timeline is a Redis sorted set: key=user:{userId}:timeline, score=timestamp
Pros
- Fast reads: Timeline is pre-computed, just read from cache (under 10ms)
- Simple timeline generation: No merge logic needed, just return cached data
- Works for 99% of users: Most users have few followers
Cons
- Fails for celebrities: User with 50M followers requires 50M cache writes per tweet
- Write amplification: 1 tweet → millions of writes
- Hotspot issues: Celebrity tweet creates massive queue backlog
- Wasted work: Many followers are inactive, but still receive writes
Math: Why pure fanout-on-write fails
Celebrity with 50M followers posts 1 tweet:
- Need to write to 50M timeline caches
- Redis write: ~1ms per write (including network)
- Single-threaded: 50M × 1ms = 50,000 seconds = 13.9 hours
- With 1,000 parallel workers: 50M / 1,000 = 50,000 writes per worker = 50 seconds
- If celebrity posts 10 tweets/day, system is constantly backlogged
Option 2: Fanout-on-Read (Pull Model)
Strategy: Don't pre-compute timelines. When a user requests their timeline, fetch recent tweets from all the people they follow and merge them.
Fanout-on-Read Process
- User requests timeline → Timeline Service queries User Service for following list
- Timeline Service queries Tweet DB: "Get 100 recent tweets from user1, user2, ..., userN"
- Merge all tweets and sort by timestamp (merge K sorted lists)
- Return top 100 tweets
Pros
- No write amplification: 1 tweet = 1 database write
- Works for celebrities: No fanout work needed
- No wasted work: Only compute timelines for active users
Cons
- Slow reads: Must query DB and merge results (100-500ms)
- Read amplification: Each timeline request queries hundreds of users
- Database load: 600K timeline reads/sec would crush the database
- Complex queries: Fetching from 200 users in a single query is expensive
Option 3: Hybrid Approach (Twitter's Solution)
Strategy: Use fanout-on-write for regular users, fanout-on-read for celebrities. Merge both at read time.
Followers > 1M} subgraph "Regular User Path" FOW[Fanout on Write] Queue[Fanout Queue] Workers[Worker Pool] Cache[Timeline Caches] FOW --> Queue Queue --> Workers Workers --> Cache end subgraph "Celebrity Path" NoFanout[Skip Fanout] CelebDB[(Celebrity
Tweet DB)] NoFanout --> CelebDB end User --> Check Check -->|No| FOW Check -->|Yes| NoFanout subgraph "Timeline Read" Request[User Requests
Timeline] TService[Timeline Service] ReadCache[Read from Cache
Fanout Tweets] ReadCeleb[Read Celebrity
Tweets] Merge[Merge & Sort] Request --> TService TService --> ReadCache TService --> ReadCeleb ReadCache --> Merge ReadCeleb --> Merge end Cache -.-> ReadCache CelebDB -.-> ReadCeleb
Hybrid Algorithm
Pros
- Best of both worlds: Fast reads for most users, no write amplification for celebrities
- Scales to billions: Handles both regular users and celebrities efficiently
- Flexible threshold: Can adjust celebrity threshold based on system load
Cons
- Complexity: Two different code paths for write and read
- Merge overhead: Timeline reads require merging cached + celebrity tweets
- Stale celebrity tweets: User may not see celebrity tweet immediately after they follow
Threshold Tuning
The "celebrity" threshold can be tuned based on system performance:
- Conservative (100K followers): More users in celebrity bucket, less write amplification, slightly slower reads
- Aggressive (10M followers): Fewer celebrities, more write amplification, faster reads for most users
- Twitter's approach: Around 1M followers threshold
- Dynamic adjustment: Can adjust threshold during peak events (e.g., elections, sports events)
6. Home Timeline Generation
Timeline Cache Structure
Each user's timeline is stored in Redis as a sorted set:
- Key: "user:{userId}:timeline"
- Score: Tweet timestamp (for chronological ordering)
- Value: Tweet ID (lightweight, actual tweet data fetched separately)
- Size: Store only 1,000 most recent tweets per user (5-10 days of content)
Timeline Read Sequence
Timeline Ranking (Optional)
Modern Twitter uses a ranked timeline instead of purely chronological:
- Engagement signals: Likes, retweets, replies count
- User affinity: How often you interact with the tweet author
- Recency: Recent tweets score higher
- Media: Tweets with images/videos score higher
- ML model: Predict probability user will engage with tweet
For simplicity, this design focuses on chronological timeline, but a ranking layer can be added on top.
Timeline Pagination
Cache Invalidation
- TTL: Timeline cache expires after 7 days (most users check daily)
- Unfollow: Don't remove tweets immediately (eventual consistency OK), next fanout will overwrite
- Deleted tweet: Remove from all caches async (can take minutes)
- Cache warming: Preload timelines for recently active users during off-peak hours
7. Search
Search Requirements
- Full-text search: Search tweet content for keywords
- Hashtag search: Find all tweets with #hashtag
- User search: Find tweets from specific users
- Real-time indexing: New tweets appear in search within seconds
- Ranked results: Most relevant or recent tweets first
Inverted Index
An inverted index maps words to the documents (tweets) containing them:
Elasticsearch Architecture
new-tweets] Indexer[Indexer Service] subgraph "Elasticsearch Cluster" Master[Master Node] Data1[Data Node 1
Shards 0-2] Data2[Data Node 2
Shards 3-5] Data3[Data Node 3
Shards 6-8] end SS[Search Service] Client[Client] TS -->|Publish tweet| Kafka Kafka --> Indexer Indexer -->|Index document| Master Master -->|Route| Data1 Master -->|Route| Data2 Master -->|Route| Data3 Client -->|Search query| SS SS -->|Query| Master Master -->|Fan out| Data1 Master -->|Fan out| Data2 Master -->|Fan out| Data3 Data1 -->|Results| Master Data2 -->|Results| Master Data3 -->|Results| Master Master -->|Aggregated| SS SS -->|Ranked results| Client
Real-Time Indexing
- User posts tweet → Tweet Service writes to database
- Tweet Service publishes to Kafka topic: "new-tweets"
- Indexer service consumes from Kafka and indexes to Elasticsearch
- Elasticsearch indexes document in memory (appears in search in 1-2 seconds)
- Flush to disk every 30 seconds (trade durability for speed)
Earlybird (Twitter's Custom Search)
Twitter built a custom real-time search engine called Earlybird:
- Real-time index: Tweets indexed within 1 second
- Time-sharded: Index partitioned by time (recent tweets in hot shard)
- Compressed posting lists: Custom compression for space efficiency
- Quality ranking: Combination of recency, engagement, user reputation
- Spam filtering: ML model to detect spam tweets at query time
Search Ranking
Ranking function combines multiple signals:
- Recency: score = 1.0 / (hours_since_tweet + 1)
- Engagement: score = (likes + retweets * 2 + replies * 3)
- User reputation: score = log(follower_count)
- Relevance: BM25 or TF-IDF score for keyword match
- Final score: weighted sum of all signals
Search Optimizations
- Typeahead cache: Cache popular search queries in Redis
- Query rewriting: Expand synonyms, fix typos, remove stop words
- Sharding: Shard index by user_id or time range
- Replicas: 3 replicas for high availability and load distribution
8. Trending Topics
Requirements
- Identify trending hashtags: Hashtags with sudden spike in usage
- Geographic trending: Different trends for different countries/cities
- Time-based: Trends change throughout the day
- Spam filtering: Ignore bot-driven hashtag spam
- Low latency: Trends update every 5-10 minutes
Trending Algorithm
Detect hashtags with sudden increase in usage compared to baseline:
Trending Pipeline
Extractor] Kafka[Kafka:
hashtag-events] subgraph "Stream Processing (Flink)" Window[Sliding Window
Aggregator
1-hour windows] Detector[Spike
Detector] SpamFilter[Spam
Filter] end Redis[(Redis:
Trending Cache)] API[Trending API] Client[Client] Tweets --> Extractor Extractor -->|hashtag, location, timestamp| Kafka Kafka --> Window Window -->|counts per hashtag| Detector Detector -->|candidates| SpamFilter SpamFilter -->|verified trends| Redis Redis --> API API --> Client
Geographic Trending
Trends are computed separately for each geographic region:
- Global trends: All tweets worldwide
- Country trends: Tweets from specific country (based on IP or user location)
- City trends: Tweets from major cities (New York, London, Tokyo, etc.)
- Storage: Redis key = "trending:{country}:{timestamp}", value = list of hashtags
Spam Filtering
Prevent bot-driven hashtags from trending:
- User diversity: Hashtag must be used by at least 50 unique users
- Rate limiting: Ignore users posting the same hashtag repeatedly
- Bot detection: ML model to identify bot accounts, weight their hashtags lower
- Manual review: Human moderators can remove inappropriate trends
Stream Processing with Apache Flink
Caching and API
- Redis cache: Store top 50 trending hashtags per location
- TTL: 10 minutes (trends refresh every 5-10 minutes)
- API endpoint: GET /trends?location=US returns top trends for US
- CDN caching: Cache trending API responses for 5 minutes
9. Database Design
Tweets Table
Users Table
Follows Table (Social Graph)
Sharding Strategy
Option 1: Shard by tweet_id
- Sharding key: tweet_id % num_shards
- Pros: Even distribution, simple
- Cons: User timeline query requires scatter-gather across all shards
Option 2: Shard by user_id
- Sharding key: user_id % num_shards
- Pros: User timeline query hits single shard
- Cons: Uneven distribution (celebrities generate far more tweets)
- Cons: Hotspot issue (celebrity tweets hit one shard repeatedly)
Recommended: Hybrid Sharding
Approach: Use Cassandra with composite partition key
- Partition key: (user_id, tweet_id / 1000000) - groups tweets by user and time bucket
- Clustering key: tweet_id (sorted within partition)
- Query: SELECT * FROM tweets WHERE user_id = ? ORDER BY tweet_id DESC LIMIT 100
- Result: Single partition read, time-ordered results
Cassandra Schema
Timeline Cache Schema (Redis)
Database Selection
- Tweets: Cassandra (write-heavy, time-series data, horizontal scalability)
- Users: PostgreSQL with read replicas (relational data, complex queries)
- Social graph: PostgreSQL or Neo4j (graph queries for recommendations)
- Timeline cache: Redis (in-memory, sorted sets, fast reads)
- Search: Elasticsearch (full-text search, inverted index)
10. Notification System
Notification Types
- Mentions: "@username mentioned you in a tweet"
- Likes: "User X liked your tweet"
- Retweets: "User X retweeted your tweet"
- Replies: "User X replied to your tweet"
- New followers: "User X followed you"
- Direct messages: "User X sent you a message"
Notification Pipeline
Event] Kafka[Kafka:
notification-events] subgraph "Notification Service" Router[Event Router] Aggregator[Notification
Aggregator] Filter[User
Preferences
Filter] Prioritizer[Priority
Queue] end Push[Push
Notification
Service] Email[Email
Service] DB[(Notification DB)] Event --> Kafka Kafka --> Router Router --> Aggregator Aggregator --> Filter Filter --> Prioritizer Prioritizer --> Push Prioritizer --> Email Prioritizer --> DB
Notification Aggregation
Instead of sending 100 notifications for 100 likes, aggregate them:
- Batching window: Wait 5 minutes before sending notification
- Aggregation: "User X and 99 others liked your tweet"
- Reduces spam: Popular tweets get thousands of likes - don't send thousands of notifications
Priority Queue
Not all notifications are equally important:
- High priority: Direct messages, mentions from verified users
- Medium priority: Likes, retweets, replies
- Low priority: New followers, trending topics
- Implementation: Separate Kafka partitions or SQS queues per priority
User Preferences
Allow users to control notification settings:
- User can mute specific users or keywords
- User can disable specific notification types (e.g., only mentions, no likes)
- User can set "quiet hours" (no notifications 10pm-8am)
- Store preferences in User DB, check before sending
Delivery Channels
- Push notifications: iOS (APNs), Android (FCM) - immediate delivery
- Email: Batched daily or weekly summary
- In-app: Notification icon in app, badge count
- SMS: High-priority only (e.g., security alerts)
Notification Storage
11. Scalability
Timeline Cache Scaling
- Redis cluster: Partition by user_id (user_id % num_shards)
- Memory requirement: 480 GB for active users (20% of 300M users)
- Cluster size: 8 Redis nodes × 64 GB = 512 GB total
- Replication: 3 replicas per shard for high availability
- Eviction: LRU eviction for inactive users (haven't logged in for 7 days)
Tweet Storage Scaling
- Cassandra cluster: Partition by (user_id, time_bucket)
- Storage requirement: 1.1 PB for 5 years of tweets
- Replication factor: 3 (each tweet stored on 3 nodes)
- Total storage: 1.1 PB × 3 = 3.3 PB
- Cluster size: 100 nodes × 40 TB = 4 PB (some overhead for compaction)
Media Storage Scaling
- S3 storage: 153 PB for 5 years of media
- Cost optimization: S3 Intelligent-Tiering (auto-move to Glacier)
- CDN: CloudFront for global distribution
- Cache hit rate: 80% cache hit for popular media (recent tweets)
- Compression: WebP for images (30% smaller than JPEG), H.265 for video
Fanout Queue Scaling
- Kafka cluster: 10 partitions for fanout queue
- Worker pool: 1,000 fanout workers (auto-scaling based on queue depth)
- Throughput: 1,000 workers × 1,400 writes/sec/worker = 1.4M timeline writes/sec
- Backlog handling: Queue can buffer several hours of work during peak
Database Read Replicas
- User DB: 1 primary + 5 read replicas (read-heavy for social graph queries)
- Tweet DB: Cassandra handles reads via partitioning (no replicas needed)
- Replication lag: 1-2 seconds acceptable for social graph (eventual consistency)
Auto-Scaling
Scale different components independently based on load:
- API servers: Scale based on request rate (target: 1000 req/sec per server)
- Fanout workers: Scale based on Kafka queue depth (target: under 1 minute lag)
- Search indexers: Scale based on Kafka queue depth
- Redis cache: Pre-provisioned (doesn't scale down, only up)
Global Distribution
CloudFront] GeoRouter[GeoDNS
Route 53] User[User] --> GeoRouter GeoRouter -->|US traffic| USE_LB GeoRouter -->|EU traffic| EUW_LB GeoRouter -->|Asia traffic| AP_LB USE_API --> CDN EUW_API --> CDN AP_API --> CDN
- Multi-region deployment: US-East, EU-West, Asia-Pacific
- GeoDNS routing: Route user to nearest region
- Data replication: Cassandra cross-region replication (async)
- Media CDN: CloudFront edge locations worldwide
- Consistency: Eventual consistency across regions (tweets appear in 1-2 seconds)
12. Tradeoffs
Fanout-on-Write vs Fanout-on-Read
| Aspect | Fanout-on-Write | Fanout-on-Read | Hybrid |
|---|---|---|---|
| Read Latency | Very fast (10ms) | Slow (200-500ms) | Fast (50ms) |
| Write Cost | High (1 write → 200 writes) | Low (1 write → 1 write) | Medium |
| Celebrity Handling | Fails (50M writes per tweet) | Works | Works |
| Complexity | Simple | Simple | Complex |
| Consistency | Eventual (fanout takes time) | Consistent | Eventual |
| Inactive Users | Wasted work (still fanout) | No waste (on-demand) | Some waste |
Write Amplification
Problem: Fanout-on-write amplifies every tweet write by the follower count.
- User posts 1 tweet → System writes to 200 timeline caches
- 1 tweet write becomes 200 cache writes (200x amplification)
- 7,000 tweets/sec × 200 = 1.4M cache writes/sec
- Mitigation: Hybrid approach skips fanout for celebrities
Read Amplification
Problem: Fanout-on-read amplifies every timeline read by the following count.
- User requests timeline → System queries tweets from 200 users
- 1 timeline read becomes 200 tweet queries (200x amplification)
- 70,000 timeline reads/sec × 200 = 14M tweet queries/sec
- Mitigation: Fanout-on-write pre-computes timelines
Consistency vs Availability
Twitter chooses availability over consistency (AP in CAP theorem):
- Timeline staleness: Timelines may be 1-2 seconds behind (acceptable)
- Follower count lag: Follower count may be incorrect for minutes (acceptable)
- Cross-region lag: Tweet posted in US may take 1-2 seconds to appear in EU
- Critical data: Tweets themselves are durable (Cassandra replication factor 3)
Storage Cost vs Latency
Timeline cache: Store tweet IDs (8 bytes) instead of full tweets (1 KB)
- Approach 1: Cache full tweets (1 KB × 1,000 tweets × 300M users = 300 TB)
- Approach 2: Cache tweet IDs (8 bytes × 1,000 tweets × 300M users = 2.4 TB)
- Tradeoff: Approach 2 saves 99% memory but requires extra DB lookup (adds 10ms)
- Decision: Use Approach 2, batch lookup tweet details (acceptable latency)
Ranking vs Simplicity
Chronological timeline: Simple, predictable, but may miss important tweets
Ranked timeline: Shows most relevant tweets first, but complex ML model
- Twitter's evolution: Started chronological, moved to ranked (controversial)
- User preference: Offer both options (switch in settings)
- Implementation: Chronological is baseline, add ranking layer on top
Real-Time vs Cost
How quickly must a tweet appear in followers' timelines?
- Real-time (1 second): Requires large worker pool, expensive
- Near-real-time (10 seconds): Smaller worker pool, cheaper, still feels real-time
- Delayed (1 minute): Very cheap, but poor user experience
- Decision: Target 10-second fanout (99% of tweets), accept occasional 1-minute delays during peak
Summary
Designing Twitter requires handling extreme scale (300M DAU, 600K reads/sec) while maintaining low latency for timeline generation. The core challenge is the fanout problem - efficiently delivering tweets to millions of followers.
Key Design Decisions
- Hybrid fanout: Fanout-on-write for regular users, fanout-on-read for celebrities, merged at timeline read time
- Timeline cache: Redis sorted sets store tweet IDs per user, enabling sub-10ms timeline reads
- Cassandra for tweets: Write-optimized, horizontally scalable, partitioned by (user_id, time_bucket)
- Asynchronous fanout: Kafka queue + worker pool decouples tweet creation from timeline delivery
- Real-time search: Elasticsearch with custom indexing pipeline for sub-second search results
- Trending topics: Stream processing with sliding window aggregation to detect hashtag spikes
- Eventual consistency: Accept 1-2 second timeline lag for availability and scalability
Capacity Estimates
- 7,000 tweets/sec (21,000 peak)
- 70,000 timeline reads/sec (210,000 peak)
- 1.1 PB tweet storage (5 years)
- 153 PB media storage (5 years)
- 480 GB timeline cache (Redis)
Technologies
- Database: Cassandra (tweets), PostgreSQL (users/graph), Redis (cache)
- Queue: Kafka (fanout, search indexing, trending)
- Search: Elasticsearch (full-text search)
- Stream processing: Apache Flink (trending topics)
- Storage: S3 + CloudFront (media)
- Load balancing: AWS ALB, Route53 (GeoDNS)
- Start with requirements and capacity estimation - this drives the architecture
- Draw the high-level architecture diagram first, then deep-dive into fanout and timeline
- Explain the celebrity problem with math - this shows you understand the constraints
- Discuss tradeoffs explicitly: write amplification vs read amplification, consistency vs availability
- Be ready to discuss edge cases: what happens when a celebrity unfollows someone? How do you handle deleted tweets?
- Twitter is a classic system design question - interviewers will push hard on fanout, so master this concept
Operational Readiness (Production-Grade)
A complete system design includes operational considerations. Here's what makes this Twitter design production-ready:
SLIs/SLOs (Service Level Indicators/Objectives)
- Timeline load latency: P99 < 200ms (includes cache read + celebrity merge + DB lookup)
- Tweet post success rate: > 99.9% (tweets are durable once 201 response returned)
- Fanout completion time: P99 < 5 seconds (time from tweet post to appearing in followers' timelines)
- Search latency: P95 < 300ms
- System availability: 99.9% (tolerate 43 minutes downtime per month)
Monitoring
- Timeline cache hit rate: Target 95%+. If drops below 90%, investigate (cache evictions, expired keys, cluster issues)
- Fanout worker lag: Time between tweet posted and fanout complete. Target P99 < 5s. Alert if lag > 30s (indicates worker pool undersized or queue backlog)
- Tweet ingestion QPS: Track write rate to Cassandra. Sudden drop may indicate upstream service failure
- Trending algorithm freshness: Time since last trending update. Alert if > 15 minutes (stream processing job may be stuck)
- Celebrity tweet merge latency: Track time spent merging celebrity tweets into timelines. Alert if P99 > 50ms (DB query too slow)
- Kafka consumer lag: Monitor fanout queue, search indexing queue, trending queue. Alert if lag > 1 minute
Alerting
- Page immediately: Fanout lag > 30 seconds for 5 minutes (users not seeing tweets from people they follow)
- Warn (no page): Timeline cache hit rate < 95% (investigate during business hours, may indicate cache sizing issue)
- Page immediately: Tweet post error rate > 0.5% (users cannot post tweets)
- Warn (no page): Cassandra disk utilization > 80% (need to add capacity soon)
- Page immediately: Timeline load error rate > 1% (users cannot view timelines)
Deployment
- Changes to fanout algorithm deployed behind feature flag (LaunchDarkly, ConfigCat)
- Stage 1: Enable for 1% of users (internal employees first)
- Stage 2: Monitor key metrics: fanout latency, timeline load time, user engagement
- Stage 3: If metrics healthy, expand to 10% → 25% → 50% → 100% over 2 days
- Rollback: Instant rollback via feature flag toggle (no code deploy needed)
- Deploy new Timeline Service version to 5% of servers
- Monitor error rate, latency, throughput for 15 minutes
- If healthy, expand to 25% → 100%
- Automated rollback if error rate > 2x baseline
Circuit Breakers
- Fanout Worker → User Timeline Cache: If Redis errors > 10% for 30 seconds, open circuit, queue fanout work to DLQ, alert on-call
- Search Indexer → Elasticsearch: If ES indexing failures > 5%, open circuit, buffer tweets locally (up to 10 minutes), retry with exponential backoff
- Timeline Service → Cassandra: If DB latency P99 > 1 second, enable aggressive caching, serve slightly stale timelines (trade freshness for availability)
Disaster Recovery
Testing
- Load testing: Simulate celebrity tweet (50M followers) in staging environment. Verify: hybrid fanout skips celebrity, timelines merge correctly, no worker pool overload
- Chaos engineering: Kill random fanout workers every day (automated via ChaosMonkey). Verify: Kafka rebalances, other workers pick up load, no user impact
- Integration tests: End-to-end test runs every deploy: post tweet → verify in DB → verify in follower's timeline (cached) → verify searchable → verify trends updated
- Synthetic tweets: Bot posts canary tweet every 5 minutes, verifies tweet appears in follower timeline within 10 seconds. Alert if canary fails (detects fanout pipeline broken)
Operational Runbooks
Document common failure scenarios and remediation steps:
- Fanout lag spike: Check Kafka consumer lag → scale workers → check for celebrity tweet storm → enable rate limiting if needed
- Timeline cache miss rate high: Check Redis cluster health → check for memory pressure → check for eviction spikes → scale Redis cluster if needed
- Cassandra hotspot: Identify hot partition (celebrity user) → add secondary index → consider sharding celebrity tweets separately
- Search down: Check Elasticsearch cluster health → check for indexing backlog → restart stuck indexer workers → enable search result caching