The Fan-Out Pattern

One event that needs to reach many recipients. The fan-out problem appears in social feeds, email systems, notification pipelines, and any system where a single write must propagate to many downstream targets. How you solve it determines whether your system is fast for readers, for writers, or breaks under celebrity-scale load.

Concept Write Amplification Social Systems Message Queues
The Core Problem

One write, many recipients

When a user posts something in a social system, every one of their followers needs to see it. At 100 followers, this is trivially solved — just write 100 records. At 100 million followers (a celebrity account), writing 100 million records synchronously before returning to the user would take hours. The system would be unusable.

This is the fan-out problem: one event that must propagate to many targets. The three approaches — push, pull, and hybrid — are fundamentally different tradeoffs between write-time complexity and read-time complexity. You can't optimize both simultaneously. Whatever you don't pay on the write path, you pay on the read path.

Why eventual consistency is the key enabler

Every fan-out solution other than synchronous push depends on the system tolerating a delay between when something is written and when all recipients see it. In a news feed, 3–5 seconds is acceptable. In a stock trading system, it isn't. Always confirm the consistency requirement before choosing a fan-out strategy. The acceptable lag is what determines which approaches are on the table.

Approach 1

Fan-out on Write — Push

When a user posts, immediately write a record into every follower's feed table. By the time any follower loads their feed, their posts are already pre-computed and waiting. Reading the feed is trivially fast — just query by userId and return pre-sorted records.

What works
  • Feed reads are instant — pre-computed
  • Read path is simple: query by userId, return results
  • No computation at read time
  • Works well for users with small follower counts
What breaks
  • Write amplification: one post → N writes (one per follower)
  • Celebrity post → millions of writes simultaneously
  • Feed table grows proportionally to followers × posts
  • Inactive followers still receive writes they may never read

Push-only works well when the follower graph is sparse and relatively balanced — no single user has dramatically more followers than the average. It breaks under celebrity load because one post triggers unbounded write amplification. A Kafka queue can make the push async (post service returns immediately, fan-out happens in background), but the total write volume is unchanged.

Approach 2

Fan-out on Read — Pull

Don't pre-compute anything at write time. When a user loads their feed, fetch posts in real time from everyone they follow, merge them, sort them, and return the result. Write path is trivially simple — just write the post to the post table. The read path is where all the work happens.

What works
  • Write path is simple: one write to post table
  • No write amplification — celebrity posts are free to write
  • Feed is always up-to-date — no staleness
  • No storage for pre-computed feeds
What breaks
  • Read path is expensive: N queries for N followees
  • If user follows 1,000 people: 1,000 lookups on every feed load
  • 5,800 reads/sec × 1,000 queries each = 5.8M DB queries/sec
  • Merge and sort at read time adds latency

Pull-only works when the follow graph is dense but the number of followees per user is small. It breaks at scale because the read-time computation — N lookups + merge + sort — grows with the number of followees. At Facebook scale where users follow hundreds of people, this is functionally impossible at acceptable latency.

Approach 3 — Used in News Feed

Hybrid — Push for regular, Pull for celebrities

The hybrid approach recognizes that the push and pull models have complementary failure modes: push fails at high follower counts (celebrity writes), pull fails at high followee counts (expensive reads). Apply each where the other fails.

The threshold is the key decision. In the News Feed session, 1M followers was chosen as the cutoff. Below 1M — fan-out service pushes to all eligible followers' feed tables. Above 1M — skip fan-out entirely. On the read path, the get service identifies which followees are celebrities (flagged in user table), fetches their recent posts directly from post cache via the hybrid get service, and merges with the pre-computed regular friend feed.

Why celebrity posts are cheap to pull

Celebrity posts get millions of read requests — they will always be at the top of the post cache's LRU eviction list. Pulling them on the read path is not expensive in practice: one cache hit per celebrity per feed load. If the celebrity had 100 followers, their post wouldn't be cache-hot and pull would be expensive. The inversion only works because celebrity posts are always cached. The same logic does not apply to a user with 1M followers who posts rarely — their posts may not be cache-hot, and pulling them would be slow.

The threshold question

The 1M threshold is a starting point, not a law. The right threshold is the point at which fan-out volume becomes a capacity problem for your fan-out service. At 58 write QPS and an average of 500 followers, fan-out handles ~29,000 feed writes per second — manageable. A single user with 100M followers posting would add 100M writes instantly. The threshold keeps any single post from dominating fan-out capacity. Tune it based on fan-out service throughput and acceptable lag.

Optimization

Inactive follower filtering

Even below the celebrity threshold, not all followers should receive fan-out writes. A user who hasn't opened the app in 30 days doesn't need their feed pre-populated — they'll get a fresh pull when they return. Skipping inactive users reduces fan-out volume significantly, because inactive users often represent a large fraction of total followers.

The fan-out service checks the is_active flag on each follower before writing to the feed table. The analytics service maintains this flag — updated from app open events. When an inactive user returns, their first feed load is a pull (slightly slower) but subsequent loads benefit from push once they're marked active again.

Why this matters more than it looks

Studies of social platforms consistently show that a large fraction of registered users are inactive at any given time — sometimes 40–60%. If your fan-out service is writing to inactive users' feed tables, it's doing 40–60% more work than necessary. At scale, that's the difference between a manageable fan-out service and one that's permanently overloaded. Inactive follower filtering is a real production optimization that most textbook answers miss.

Quick Reference

Comparison table

DimensionPush (write)Pull (read)Hybrid ✓
Write costHigh (N writes)Low (1 write)Medium (N writes for non-celebrity)
Read costLow (pre-computed)High (N lookups at read time)Low (pre-computed + cache pull)
Celebrity handlingBreaks (100M writes)Fine (1 write)Pull model above threshold
Inactive usersWasteful writesNo wasteFilter inactive from push
Feed freshnessAlways currentAlways current3-5s eventual consistency
ComplexityLowLowHigh (two paths, threshold logic)
Cross-reference

Systems that use this concept

Referenced from these designs
News Feed — fan-out service deep dive Notification System — coming soon Email Campaign System — coming soon