Обновлено: 25 марта 2026 г. в 04:52

Twitter/X

medium

Классическая задача: feed generation, fanout on write vs read, trending topics, search.

Twitter-подобная система быстро упирается в асимметрию записи и чтения, fanout знаменитостей, кэш таймлайна и цену мгновенного обновления ленты.

Кейс помогает сравнить fanout-on-write и fanout-on-read, понять роль timeline cache, social graph и очередей перестроения ленты.

В интервью и design review он полезен тем, что проверяет, умеете ли вы выбирать стратегию под профиль нагрузки, а не под красивую идею.

Pipeline Thinking

Критичны ingestion, partitioning, deduplication и задержки между стадиями обработки.

Serving Layer

Решения по индексам, cache locality и query path определяют пользовательскую latency.

Consistency Window

Явно фиксируйте, где допустима eventual consistency и где требуется stronger guarantees.

Cost vs Freshness

Балансируйте скорость обновления данных и стоимость вычислений/хранения.

Введение

Проектирование Twitter — это классическая задача на собеседованиях по System Design. Ключевые challenge: доставка твитов миллионам фолловеров, генерация персонализированной ленты в реальном времени и определение трендовых тем. Этот кейс демонстрирует trade-offs между fanout-on-write и fanout-on-read подходами.

Функциональные требования

  • Post Tweet — публикация твита (до 280 символов, медиа)
  • Home Timeline — лента твитов от людей, на которых подписан
  • User Timeline — все твиты конкретного пользователя
  • Follow/Unfollow — подписка на пользователей
  • Like/Retweet — взаимодействие с твитами
  • Search — поиск по твитам и пользователям
  • Trending Topics — популярные темы в реальном времени
  • Notifications — уведомления о mentions, likes, retweets

Нефункциональные требования

  • Scale: 500M пользователей, 200M DAU
  • Tweets/day: 500M новых твитов
  • Read-heavy: соотношение read:write = 1000:1
  • Timeline latency: <200ms для home timeline
  • Eventual consistency: твит может появиться с задержкой 5-10 сек
  • Availability: 99.99% uptime
  • Celebrity problem: пользователи с 100M+ фолловеров

Traffic estimation: 200M DAU × 100 timeline reads/day = 20B reads/day ≈ 230K QPS (read). 500M tweets/day ≈ 6K QPS (write).

Core Problem: Feed Generation

Главная архитектурная проблема Twitter — как эффективно генерировать home timeline. Есть два фундаментальных подхода: fanout-on-write (push) иfanout-on-read (pull).

Fanout-on-Write (Push)

При публикации твита он сразу записывается в timeline cache каждого фолловера.

  • ✅ Быстрое чтение timeline (уже готов)
  • ✅ Простая логика при чтении
  • ❌ Медленная публикация для celebrities
  • ❌ Много storage (дублирование)
  • ❌ Wasted work для неактивных users

Fanout-on-Read (Pull)

Timeline собирается на лету при запросе: получаем список followees, их последние твиты, merge.

  • ✅ Быстрая публикация (O(1))
  • ✅ Меньше storage
  • ✅ Нет wasted work
  • ❌ Медленное чтение (много запросов)
  • ❌ Сложный merge в реальном времени

Hybrid Approach (Twitter's Solution)

Twitter использует гибридный подход: fanout-on-write для обычных пользователей и fanout-on-read для celebrities (users с >10K фолловеров).

Interactive Architecture Map

Переключайте путь, чтобы подсветить fanout-on-write или fanout-on-read

Tweet Ingestion

API + Media Upload

Tweet Service

Store tweet + detect celebrity

Regular Users (fanout-on-write)

Fanout Service

Write to followers timeline cache

Timeline Cache

Redis sorted sets per user

Celebrities (fanout-on-read)

Celebrity Store

Separate tweet store

Merge on Read

Combine with cached feed

Timeline Service

Rank + filter + deliver

Celebrity Detection

Порог ~10,000 фолловеров. При публикации: если followers > threshold, твит НЕ fanout'ится, а хранится в отдельном "celebrity tweets" store. При чтении timeline: merge pre-computed feed + свежие celebrity tweets.

Timeline Cache Architecture

Redis Timeline Structure:

# Каждый user имеет свой timeline в Redis
# Key: timeline:{user_id}
# Value: Sorted Set (score = timestamp, member = tweet_id)

ZADD timeline:12345 1705234567 "tweet_abc123"
ZADD timeline:12345 1705234890 "tweet_def456"

# Получение последних 100 твитов
ZREVRANGE timeline:12345 0 99

# Timeline storage estimation:
# 200M users × 800 tweets × 8 bytes = 1.28 TB
# С replication ×3 = ~4 TB Redis cluster

Timeline Limits

  • Max 800 tweets в timeline cache
  • Старые твиты удаляются (ZREMRANGEBYRANK)
  • Для старых твитов — запрос в DB

Fanout Workers

  • Async processing через Message Queue
  • Batch updates для эффективности
  • Retry logic для failed fanouts

Trending Topics

Определение трендов — это задача real-time stream processing. Нужно отслеживать частоту появления hashtags и keywords с учётом временного окна и velocity.

Trending Pipeline

Нажмите на этап, чтобы подсветить соответствующую часть пайплайна

Tweet Stream

Kafka topic

Extract

Hashtags, entities

Filter

Spam + stop words

Scoring

Sliding window + decay

Trend Cache

Redis sorted sets

Count-Min Sketch

Probabilistic data structure для approximate counting. Экономит память при подсчёте миллионов уникальных hashtags. Trade-off: небольшая погрешность (~1-2%) за O(1) space.

Sliding Window

Обычно 5-15 минутное окно с decay. Hopping window (обновление каждую минуту) для smooth transitions. Tumbling window для hourly aggregates.

Trend Scoring Formula

# Simplified Twitter Trending Score

def calculate_trend_score(topic, current_window):
    # Current velocity: tweets per minute in last 5 min
    current_count = count_in_window(topic, minutes=5)
    
    # Baseline: average tweets per 5-min over past 7 days
    baseline_count = get_baseline(topic, days=7)
    
    # Velocity ratio: how much faster than normal
    if baseline_count > 0:
        velocity_ratio = current_count / baseline_count
    else:
        velocity_ratio = current_count * 10  # New topic bonus
    
    # Recency weight: exponential decay
    recency_weight = sum(
        tweet.weight * exp(-lambda * (now - tweet.timestamp))
        for tweet in current_window.tweets
    )
    
    # Engagement boost
    engagement_score = (likes + retweets * 2 + replies * 3) / total_tweets
    
    # Final score
    score = velocity_ratio * recency_weight * (1 + log(engagement_score))
    
    # Spam/bot filtering
    if unique_users_ratio < 0.3:  # Too few unique users
        score *= 0.1  # Heavy penalty
    
    return score

Timeline Ranking

Современный Twitter использует ML-based ranking вместо чистого chronological order. Модель предсказывает вероятность engagement для каждого твита.

Ranking Features:

Tweet Features
  • Age of tweet
  • Has media (image/video)
  • Has links
  • Tweet length
  • Current engagement rate
  • Author's average engagement
User Features
  • Historical interactions with author
  • Topic interests
  • Activity patterns (time of day)
  • Social graph proximity
  • Device type
  • Session context

Two-Pass Ranking

Pass 1 (Candidate Generation): Быстрый retrieval ~1000 candidate tweets из timeline cache + celebrity tweets + "In case you missed it".

Pass 2 (Ranking): ML model scores каждого candidate. Top 50 показываются пользователю. Inference <50ms.

Search Architecture

Search Pipeline

Переключайте между indexing и query flow

Indexing Flow

Ingestion

Tweet → tokenize

Realtime Index

Lucene in-memory shards

Cold Index

On-disk segments

Query Flow

Parse Query

Terms + filters

Fanout to Shards

Parallel search

Merge + Rank

Recency + engagement

Index latency ~10s
Search latency p99 < 200ms

Data Model

# Core Tables

tweets {
    tweet_id: UUID (Snowflake ID)
    user_id: UUID
    content: VARCHAR(280)
    media_urls: JSON
    created_at: TIMESTAMP
    reply_to_tweet_id: UUID (nullable)
    retweet_of_id: UUID (nullable)
    quote_tweet_id: UUID (nullable)
    like_count: INT
    retweet_count: INT
    reply_count: INT
}

users {
    user_id: UUID
    username: VARCHAR(15)
    display_name: VARCHAR(50)
    bio: VARCHAR(160)
    follower_count: INT
    following_count: INT
    is_verified: BOOLEAN
    is_celebrity: BOOLEAN  # follower_count > 10K
    created_at: TIMESTAMP
}

follows {
    follower_id: UUID
    followee_id: UUID
    created_at: TIMESTAMP
    PRIMARY KEY (follower_id, followee_id)
}

# Denormalized for read performance
user_timelines (Redis Sorted Set)
    Key: timeline:{user_id}
    Score: tweet_timestamp
    Member: tweet_id

# Celebrity tweets (separate store)
celebrity_tweets (Redis Sorted Set)
    Key: celebrity:{user_id}
    Score: tweet_timestamp
    Member: tweet_id

Snowflake ID Generation

Twitter разработал Snowflake для генерации уникальных, сортируемых ID без централизованной координации.

# Snowflake ID Structure (64-bit)
┌────────────────────────────────────────────────────────────────┐
│ 1 bit │   41 bits timestamp   │ 10 bits │    12 bits          │
│unused │   (milliseconds)      │machine  │   sequence          │
│       │   since epoch         │   ID    │   number            │
└────────────────────────────────────────────────────────────────┘

# Properties:
# - Roughly sortable by time
# - No coordination needed
# - 4096 IDs per millisecond per machine
# - 69 years before overflow

# Example ID: 1605978261000000000
# Timestamp: 2020-11-21 12:31:01 UTC
# Machine: 42
# Sequence: 0

def generate_snowflake_id(machine_id, last_timestamp, sequence):
    timestamp = current_time_ms() - TWITTER_EPOCH
    
    if timestamp == last_timestamp:
        sequence = (sequence + 1) & 0xFFF  # 12 bits
        if sequence == 0:
            timestamp = wait_next_millis(last_timestamp)
    else:
        sequence = 0
    
    id = (timestamp << 22) | (machine_id << 12) | sequence
    return id, timestamp, sequence

High-Level Architecture

High-Level Architecture

Выберите поток, чтобы подсветить ключевые компоненты

Edge & Routing

Clients

Web + Mobile

CDN

Static + media

Load Balancer

Traffic routing

API Gateway

Auth + rate limit

Core Services

Tweet Service

Write tweets

Timeline Service

Feed assembly

Search Service

Query layer

Trending Service

Stream processing

User Service

Profiles + graph

Async + Data

Message Queue

Kafka + fanout

Tweets DB

Primary storage

Timeline Cache

Redis ZSETs

Search Index

Lucene/ES

Trending Cache

Redis sorted sets

Graph DB

Followers graph

Interview Tips

Ключевые trade-offs

  • Fanout-on-write vs fanout-on-read — объясни hybrid подход
  • Consistency vs latency — eventual consistency acceptable
  • Storage vs compute — pre-compute vs on-demand
  • Accuracy vs performance в trending (Count-Min Sketch)

Частые follow-up вопросы

  • Как обрабатывать celebrity with 100M followers?
  • Как масштабировать fanout workers?
  • Как определить spam trends?
  • Как реализовать "For You" персонализацию?

Дополнительные ресурсы

Связанные главы

Чтобы отмечать прохождение, включи трекинг в Настройки