05. Twitter/X Hybrid Fanout Feed Engine
Push-Write + Pull-Read + O(N log K) Binary Max-Heap Cursor Merger slashing database load from 20,600 req/s to 1,201 req/s.
05. Twitter/X Hybrid Fanout Feed Engine
Production Paradigm: Asymmetric Push-Write for Normal Users + Pull-Read for Celebrities + O(N log K) Binary Max-Heap Keyset Cursor Merger + Valkey Capped Timeline Mixer.
💥 The Real-World Bottleneck
Timeline feeds (Twitter/X, Facebook News Feed, Threads) represent one of the most notorious challenges in distributed systems:
- The Celebrity Write Explosion (Push Breakdown):
- When a celebrity with 30,000,000 followers posts a tweet, pushing that tweet into 30 million user timeline inboxes requires 30 million Redis writes and database transactions. The system stalls for minutes.
- The Follower Read Explosion (Pull Breakdown):
- If a normal user follows 2,000 accounts and you pull tweets on every feed refresh (
WHERE author_id IN (...) ORDER BY created_at DESC LIMIT 20), the relational query must scan, filter, and sort across millions of rows, blowing up database IOPS and memory.
- If a normal user follows 2,000 accounts and you pull tweets on every feed refresh (
🏛️ High-Concurrency Architecture
This blueprint implements the Twitter/X Hybrid Fanout Architecture (proven in GonnaCrash Level 22 fanout_feed):
Key Architectural Phases
- Follower Threshold Branching (Celebrity Cutoff = 25,000):
- Authors with
< 25,000followers fan out asynchronously on write into each follower's Valkey timeline inbox. - Authors with
>= 25,000followers skip fanout entirely and write only to their own single outbox list.
- Authors with
- Capped Sorted Set Inboxes:
- Follower timelines are capped at 800 items using atomic Redis Lua scripts (
LUA_ZADD_CAPPED_TIMELINE). Older items are pruned automatically, keeping RAM usage deterministic.
- Follower timelines are capped at 800 items using atomic Redis Lua scripts (
- $O(N \log K)$ Binary Max-Heap Cursor Merger:
- When a user requests their feed, the engine fetches the user's pre-computed timeline plus the outboxes of any followed celebrities.
- A specialized binary max-heap merges these $K$ streams sorted by
createdAtMs DESC, id DESCin $O(N \log K)$ time.
- Strict Keyset Cursor Pagination:
- Uses Base64URL-encoded deterministic cursors (
createdAtMs + id), eliminating offset pagination drift when new tweets are posted.
- Uses Base64URL-encoded deterministic cursors (
🧩 Composed Functional Slices
| Slice | File Path | Core Role |
|---|---|---|
hybrid-fanout-engine | packages/hybrid-feed/src/hybrid-fanout-engine.ts | Asymmetric push/pull classification and database query generation with SQL injection safety |
k-way-cursor-merger | packages/hybrid-feed/src/k-way-cursor-merger.ts | Deterministic $O(N \log K)$ Binary Max-Heap merger (kWayMergeFeeds, BinaryMaxHeap, encodeTimelineCursor) |
valkey-timeline-mixer | packages/hybrid-feed/src/valkey-timeline-mixer.ts | Atomic Lua-capped sorted set timeline storage (LUA_ZADD_CAPPED_TIMELINE) and L1 celebrity cache |
progressive-tier | packages/hybrid-feed/src/progressive-tier.ts | Tier 0 SME, Tier 1 Growth, and Tier 2 Big-Tech hybrid feed benchmark runners |
💻 Code Implementation
1. Copy Slices into Your Project
mkdir -p src/lib/feed
cp packages/hybrid-feed/src/hybrid-fanout-engine.ts src/lib/feed/
cp packages/hybrid-feed/src/k-way-cursor-merger.ts src/lib/feed/
cp packages/hybrid-feed/src/valkey-timeline-mixer.ts src/lib/feed/2. Implementation (src/services/feed-service.ts)
import { HybridFanoutEngine } from '@/lib/feed/hybrid-fanout-engine';
import { kWayMergeFeeds, encodeTimelineCursor, decodeTimelineCursor } from '@/lib/feed/k-way-cursor-merger';
import { ValkeyTimelineMixer } from '@/lib/feed/valkey-timeline-mixer';
const CELEBRITY_THRESHOLD = 25000;
const mixer = new ValkeyTimelineMixer({ maxTimelineItems: 800 });
export async function publishPost(authorId: string, followerCount: number, post: { id: string; createdAtMs: number }) {
if (followerCount >= CELEBRITY_THRESHOLD) {
// Mode: Celebrity Pull Bypass (Write once to outbox)
await mixer.appendCelebrityOutbox(authorId, post);
} else {
// Mode: Push Write to follower inboxes
const followerIds = await getFollowerIds(authorId);
await mixer.fanoutPostToInboxes(followerIds, post);
}
}
export async function getHomeTimeline(userId: string, cursorStr?: string, limit: number = 20) {
const cursor = cursorStr ? decodeTimelineCursor(cursorStr) : undefined;
// 1. Fetch user inbox from Valkey
const inboxItems = await mixer.getUserInbox(userId);
// 2. Fetch outboxes of followed celebrities
const followedCelebrityIds = await getFollowedCelebrities(userId);
const celebrityFeeds = await Promise.all(
followedCelebrityIds.map(celebId => mixer.getCelebrityOutbox(celebId))
);
// 3. Merge all streams in O(N log K) via Binary Max-Heap
const allStreams = [inboxItems, ...celebrityFeeds];
const { items, nextCursor } = kWayMergeFeeds(allStreams, { limit, cursor });
return {
items,
nextCursor: nextCursor ? encodeTimelineCursor(nextCursor) : null,
};
}📊 Production Scale Benchmark & Verification
Verified on the GonnaCrash Level 22 (fanout_feed) simulation:
| Architecture Pattern | Pure Pull-on-Read | Pure Push-on-Write | Kido Hybrid Fanout |
|---|---|---|---|
| Write Amplification | 1 write / tweet | 30M writes / celeb tweet (💥) | 1 write for celebs, bounded fanout for normal |
| Read Load on Relational DB | 20,600 req/sec | 1,201 req/sec | 1,201 req/sec (94% reduction) |
| K-Way Merge Latency | N/A | N/A | < 1.2 ms for 10 streams |
| Pagination Consistency | Prone to duplicate items | Prone to duplicate items | 0 Duplicate Posts (Base64 Keyset Cursor) |
04. Zero-Cost 3-Tier Storage Lifecycle
AWS SigV4 Pure WebCrypto Presigning + Cloudflare R2 $0 Egress Cap + imgproxy Cold Transcoder eliminating 85% of cloud asset costs.
06. Zero-Downtime Blue-Green & Shadow Canary Gate
Inode-Safe Caddy Reload + Shadow Parity Mirroring + Graceful Connection Drain eliminating Next.js Server Action 404s.