Design Twitter's tweet posting, home timeline, follow graph, and fan-out with Redis and Cassandra.
Published April 21, 2025
Functional: Post tweets, Follow users, View home timeline, Like/Retweet Scale: 300M DAU, 500M tweets/day, 100B timeline reads/day
Generating a user's timeline = aggregating tweets from everyone they follow. If a user follows 1000 people, a naive query SELECT * FROM tweets WHERE user_id IN (followees) ORDER BY created_at is extremely expensive at scale.
Fan-out on Write (Push model)
When User A tweets, push the tweet ID to every follower's timeline cache.
Tweet posted by User A
→ Message queue
→ Fan-out worker reads follower list (500 followers)
→ Writes tweet_id to each follower's timeline in Redis
Timeline read:
→ Read from Redis cache (O(1) per user)
→ Extremely fast!
✅ Fast reads ❌ Write amplification for celebrities (Katy Perry has 150M followers → 150M Redis writes per tweet)
Fan-out on Read (Pull model)
When user views timeline, query tweets from all followees.
Timeline read for User B (follows 1000 people):
→ Fetch last 20 tweet IDs from each of 1000 followees
→ Merge-sort 20,000 tweets → top 20
→ Cache result per user
✅ No write amplification ❌ Very slow for users following many people; complex to cache
Hybrid (Twitter's actual approach):
-- Tweets: Cassandra (time-series, high write)
tweets: tweet_id (Snowflake), user_id, text, media_urls, created_at
-- Follow graph: Graph DB or Cassandra
follows: follower_id, followee_id, followed_at
-- Timeline cache: Redis sorted set per user
-- key = "timeline:{userId}"
-- score = tweet timestamp, value = tweet_id
API Gateway → Load Balancer
↓
[Tweet Service] → Cassandra (tweets)
↓
[Fanout Service] ← message queue
↓
[Redis Timeline Cache] ← ZADD timeline:{userId} score=ts value=tweetId
↓
[Timeline Service] → hydrate tweet IDs → return tweets