DEV Community

Cover image for Designing X (Twitter): How to Scale 500K Writes/Min, Live Polling, and Social Graphs
The Architect
The Architect

Posted on

Designing X (Twitter): How to Scale 500K Writes/Min, Live Polling, and Social Graphs

Designing a platform like X (formerly Twitter) requires handling extreme read-and-write asymmetric scale. Operating at 500,000 posts per minute alongside tens of millions of timeline requests per second renders traditional monolithic architectures and standard relational databases unusable.

In this deep dive, we break down the core system architecture behind X, focusing on high-throughput post ingestion, live polling without background timers, real-time WebSocket debouncing, and dual-adjacency list social graph storage.


1. High-Level Requirements & Scale

Functional Requirements

  • Post Creation: Support rich text, media (images, videos, GIFs), polls, and metadata.
  • Engagement: Real-time and lazy capabilities to like, comment, bookmark, and share posts.
  • Social Graph: Ability to follow, block, and mute users.
  • Search & Trends: Ingestion pipelines for real-time indexed search and trending topics.
  • Analytics: Post-level metrics (likes, retweets, impressions) and user-level engagement tracking.

System Scale

  • Ingestion: ~500,000 posts created per minute (~8,300+ writes/sec baseline, higher during peak events).
  • Read Throughput: Tens of millions of timeline requests per second.
  • Latency SLA: Synchronous write path under 15ms; asynchronous event generation under 2ms.

2. Distributed Microservices Architecture

X cannot operate as a monolith. Processing is split into decoupled microservices communicating asynchronously over event streams and synchronously via gRPC.

X system architecture

Path Breakdown

  1. Synchronous Write Path (~15ms): Post ingestion writes directly to the primary database (Cassandra / Manhattan or CockroachDB) using partition keys (user_id) and sort keys (post_id).
  2. Asynchronous Event Emission (~2ms): The Post Service emits a lightweight post-events event to Apache Kafka.
  3. Downstream Pipeline Isolation: Specialized consumer groups process Kafka topics independently:
    • Fan-Out Workers: Push timeline payloads into Redis memory.
    • Search Indexers: Ingest inverted indexes into Elasticsearch.
    • Flink Analytics: Perform sliding-window analytics and dump history to ClickHouse OLAP.
    • Safety Engines: Run ML content moderation rules.

3. Deep-Dive: Post Creation & Media Storage

When a user submits a post, the platform splits the payload into two distinct pathways based on asset type:

X post creation workflow

Why Cassandra / Manhattan Wide-Column Storage?

  • Masterless Architecture: Any node can accept writes without a single master bottle-necking throughput.
  • LSM-Tree Storage Engine: Log-Structured Merge (LSM) trees convert random writes into sequential memory append operations (writing to Memtables and Sables), making database writes nearly as fast as writing to RAM.
  • Tunable Consistency: Social feeds do not demand strict ACID compliance. Accepting eventual consistency across regions (e.g., a 100ms lag before a global follower sees a post) trades absolute consistency for maximum write availability.

4. Comment Thread Data Modeling

On X, comments are treated internally as standard posts, enriched with hierarchical thread metadata.

Comment Payload Example

{
  "post_id": "9900112233",
  "author_id": "usr_bob",
  "text": "Great system design breakdown!",
  "reply_metadata": {
    "parent_post_id": "181920394820",
    "root_post_id": "181920394820"
  },
  "created_at": 1722506400
}
Enter fullscreen mode Exit fullscreen mode

Partitioning Strategy over Secondary Indexing

Creating secondary indexes on high-throughput wide-column databases like Cassandra introduces catastrophic performance degradation at scale.

Instead, comment reads leverage existing partition key semantics:

  • Fetching comments for a viral post does not require secondary indexing queries across nodes.
  • Comments are queried by parent_post_id or root_post_id, leveraging localized table partitions to fetch nested comment trees efficiently.

5. High-Scale Live Polling Architecture

Handling live poll expiration and instantaneous vote aggregation without crashing databases or background infrastructure requires specific structural design patterns.

The Anti-Pattern: Background Cron Timers

Running background timers or cron jobs to mark millions of expiring polls as "closed" at 24 hours is unscalable.

Solution 1: Stateless Expiration Validation

Every poll contains a immutable created_at timestamp. Validation is stateless:

  1. When a client renders or attempts to click a poll option, the application compares current_time - created_at >= 24_hours.
  2. If true, the vote UI is disabled client-side and rejected server-side immediately without contacting primary storage.

X polls expiration

Solution 2: Write-Behind Caching (Redis to Cassandra)

Directly incrementing database rows on Cassandra for every incoming vote causes severe row locking and write bottlenecks under viral load.

  1. Atomic In-Memory Increment: incoming votes run INCRBY against an in-memory Redis cluster, capable of hundreds of thousands of atomic operations per second per node.
  2. Lazy Database Persistence: Background workers flush cumulative count snapshots from Redis into Cassandra asynchronously every few seconds (Write-Behind Cache).

Solution 3: Throttling & WebSocket Aggregation

Pushing raw WebSocket events for millions of concurrent live votes will crash client devices and exhaust connection server sockets.

  • Rate Aggregation & Debouncing: Worker processes consume raw vote updates, aggregate incoming tallies over a 500ms window, and emit 1 aggregated frame per second per poll to the WebSocket layer.
  • Result: Even if a poll receives 10,000 votes in half a second, the WebSocket layer broadcasts a single consolidated snapshot frame. Network and client CPU overhead drops by over 99% while preserving real-time UI dynamics.

6. Engagement Metrics & Social Graph Architecture

Lazy Engagement Updates

Metrics like Likes, Retweets, and Bookmarks are not pushed instantly over real-time connections to every active feed viewer. Doing so would exhaust system bandwidth. Metrics are fetched lazily when a user actively clicks a post, inspects details, or manually pulls to refresh their feed.


Social Graph Storage: Dual-Adjacency Lists

Social networks face two opposing access pattern queries:

  • Query A (Forward Graph): "Who is User X following?" (Required when building User X's feed).
  • Query B (Inverted Graph): "Who follows User X?" (Required when fanning out User X's post to their followers).

Representing these queries within a single relational table (follower_id, followee_id) leads to heavy IO read bottlenecks. X isolates graph queries into Dual-Adjacency Lists:

Forward Graph Table (following_by_user):
User_123 -> [User_456, User_789, User_999]

Inverted Graph Table (followers_by_user):
User_456 -> [User_123, User_555, User_888]
Enter fullscreen mode Exit fullscreen mode

Sparse Directed Edges: Block & Mute Filtering

Blocks and Mutes are sparse compared to follow graphs. They are stored as fast in-memory sets inside Redis or evaluated via active session Bloom Filters:

# Redis Set Data Structure for Block/Mute queries
SET mute:user_123 -> { user_456, user_789 }
SET block:user_123 -> { user_456 }
Enter fullscreen mode Exit fullscreen mode

When building a timeline, post IDs authored by users within a consumer's active mute or block set are filtered out in-memory prior to timeline presentation.


Read detailed architecture

X Decoded: The Engineering Architecture Handling 500K Tweets/Min

💡 Get the Full 10-Page System Design Guide

Subscribe to The Tech Builder Newsletter to instantly get the full, unredacted guide for free.

Every week, subscribers receive:

  • 🎯 Deep-dive production postmortems & system design trade-off analysis.
  • 🛠️ Real-world architecture playbooks for Senior ICs, Tech Leads, and Architects.
  • 🎁 Instant Bonus: Get the Full 6-Month Prep Tracker & Study Schedule + 10-Page System Design Cheat Sheet immediately upon subscribing.

👉 Get the Full System Design Cheat Sheet

Top comments (0)