Free preview

Fan-Out Models

In one line: that building block deferred fan-out to that building block. This is that building block, and it says: "the fully constructed newsfeed is sent to the client using one of the fan-out approaches." Six words, no explanation. So here it is.

The problem in this chapter's numbers

Average connections per user  = 300 friends + 250 pages = 550
Daily active users            = 500 million
Assume each posts ~2 times/day

Every post must reach the feeds of everyone connected to its author. The question is when that work happens.

Push — fan-out on write

When a post is created, write it into every follower's feed immediately.

The write amplification, computed

Work it out with the chapter's own assumptions:

Posts per day        = 500M users x 2       = 1 billion posts/day
Fan-out per post     = 550 connections
Timeline insertions  = 1B x 550             = 550 BILLION writes/day
Per second           = 550B / 86,400        = ~6.4 MILLION writes/second

6.4 million timeline writes per second — against the 58,000 read requests per second Lesson 3 computed.

So a push design does roughly 110 write operations for every read request. That is the number the chapter's estimation should have produced and did not.

Two things follow immediately.

The write path is the system. All the capacity is on the side nobody sees. A "read-heavy" product is, internally, overwhelmingly write-heavy.

Reads become trivial. Alice's feed already exists; opening the app is one cache read. That is the entire point, and it is why push is the default despite the arithmetic.

Where push breaks — and pages make it worse than Twitter

Push fails on the tail of the distribution, and this chapter's entity model makes the tail fatter than Twitter's.

A user with 300 friends generates 300 writes per post. Fine. But pages and groups have no such bound:

Average user page      ->      thousands of followers
Large brand page       ->      tens of millions

One post from a major page is tens of millions of writes. Three consequences, all from that building block and all worse here:

Latency. At a million fan-out writes per second, 10 million followers takes 10 seconds before the last feed updates.

Burst. Several large pages posting together produces a spike no steady-state plan absorbs.

Waste. Most followers are inactive. You pay per follower; the value is per active reader.

The structural point: this is a power law, and the chapter's own assumptions contain it. An average of 250 pages per user means pages have, on average, enormous follower counts — and the distribution is far more skewed than the mean suggests.

Any design that costs O(followers) fails when follower counts are power-law distributed, because the extreme cases are both very expensive and impossible to ignore.

Pull — fan-out on read

Write once. Assemble the feed when the user asks.

Pull's cost is 550 lookups on every app open

Reads/second      = 58,000
Connections each  = 550
Lookups/second    = 58,000 x 550 = ~32 MILLION reads/second

Plus a merge, plus ranking, all inside Lesson 2's 2-second budget.

That is better than push's 6.4 million writes in raw operation count — reads are cheaper than writes, and these can be parallelized and cached. But it has two properties that make it wrong as a default:

The work repeats. A user who opens the app ten times a day — exactly the chapter's assumption — pays the 550-way merge ten times. Push pays once per post and serves every subsequent read for free. Push amortizes; pull recomputes.

It puts ranking on the critical path. Lesson 10's ranking service is computationally heavy — Facebook evaluates "thousands of features" per person. Doing that synchronously inside 2 seconds, 58,000 times a second, is a very different capacity problem from doing it offline.

Pull's real virtue is that it is immune to follower count. A page with 50 million followers costs exactly one write, and the read cost is borne only by users who actually show up.

The hybrid

Split by author, not by reader — and the asymmetry is what makes it cheap

Author has FEW followers   ->  PUSH  (write into their followers' feeds)
Author has MANY followers  ->  PULL  (fetch at read time)

Alice's feed = her precomputed list  +  merge in the few large accounts she follows

This is not averaging two mediocre options. Each is applied where its dominant cost is smallest:

Ordinary accountsLarge pages / celebrities
How many existMillionsThousands
Followers eachHundredsMillions
Push costCheapRuinous
Pull cost per readerWould be 550 lookupsA handful
StrategyPUSHPULL

The arithmetic that makes it work: of Alice's 550 connections, perhaps 10 are large pages. So her read-time merge is 10 lookups, not 550 — cheap enough to sit inside the latency budget, while the other 540 were pushed.

Recompute the write load with the hybrid: large accounts stop fanning out entirely, and they are exactly the accounts responsible for the largest share of the 550-billion-write total. Removing the top of a power law removes most of the mass.

Apply each strategy to the population where its dominant cost is smallest. The hybrid works precisely because the distribution is skewed — the same property that broke push is what makes the hybrid cheap.

Two refinements that matter more than the threshold itself

Fan out only to active users. The design's own requirement says "affix new incoming posts for all active users" — and treats it as a qualifier rather than the optimization it is.

With 1 billion registered users and 500 million daily active, half the user base never sees a fanned-out post on a given day. Over a month the ratio is far worse. Skipping inactive users removes most of the fan-out cost outright, and their feeds are rebuilt on demand when they return.

The chapter's design already supports this — Lesson 8 says the web server either "calls the newsfeed generation service to generate feeds, because some users don't often visit the platform" or "fetches the pre-generated newsfeed for active users." That is the hybrid applied to readers rather than authors, and it is the single largest saving available.

So there are really two hybrid axes:

By AUTHOR:  push for small accounts, pull for large ones
By READER:  precompute for active users, generate on demand for lapsed ones

Set the threshold by cost, not follower count. Push when followers × probability-of-being-read is below the cost of a read-time merge. That captures the real insight: an account with a million inactive followers should be pulled even when a smaller account with engaged followers should be pushed.

Store references, not values — the correction the estimate needed

Lesson 3 found the 56 PB figure counts media in every feed that contains it. This is where that is resolved.

A feed entry is a reference:

BAD:   feed = [full post with media, full post, ...]
GOOD:  feed = [<Post_ID, User_ID>, <Post_ID, User_ID>, ...]

The design's design does exactly this — Lesson 9 states feeds are stored "in the form of <Post_ID, User_ID> in the newsfeed cache" and hydrated at read time from the post and user caches.

Three reasons it must be references:

Storage. The difference between Lesson 3's 56 PB and a fraction of a petabyte.

Mutability. Like counts, comment counts, and edits change constantly. With references they are fetched fresh; with copies you would rewrite hundreds of millions of feeds.

Deletion. A removed post is filtered in one place rather than chased through every feed containing it.

Fan out references, not values — the same rule that building block reached, and the reason the read path always has a hydration step.

What the hybrid costs: the merge is not free, but it is already there

Honest accounting. The hybrid means every feed read now does a merge — precomputed entries plus pulled large-account entries — rather than a single cache lookup.

That sounds like it gives back push's main advantage. It does not, for a reason specific to a ranked feed:

The read path already had to do work. Unlike Twitter's chronological timeline, a newsfeed is ranked. Lesson 10's ranking, ad insertion, deleted-post filtering, and per-viewer visibility checks all happen at read time regardless. Merging in a handful of pulled posts is marginal work in a stage that must exist.

Which is a general principle worth carrying: if a read path already has an assembly stage, adding a pull component to it is nearly free. The hybrid is cheap in ranked feeds and more expensive in purely chronological ones — which is why it is the standard answer here.

Key takeaway

Push costs ~6.4 million timeline writes per second against 58,000 reads — roughly 110 writes per read — so a "read-heavy" product is internally write-heavy, and it breaks on pages with millions of followers, a fatter tail than Twitter's because pages are unbounded. Pull is immune to follower count but recomputes on every open and puts ranking on the critical path. The hybrid splits by author, and works because of the same skew that broke push: removing the top of a power law removes most of the mass. Two refinements matter more than the threshold: fan out only to active users — half the registered base, and the design treats it as a passing qualifier — and set the threshold by expected cost, not follower count. Feeds store <Post_ID, User_ID> references, not values, which is both why the read path hydrates and why Lesson 3's 56 PB was wrong.

Interview signal by level

LevelWhat a strong answer sounds like
L4"We precompute each user's feed when someone they follow posts, so reading is a single lookup."
L5Names the failure: "push makes reads cheap but a page with 10 million followers means 10 million writes per post. Pull fixes that but re-merges 550 connections on every app open. So: hybrid — push for ordinary accounts, pull for large ones."
Staff+Quantifies and adds the second axis: "push is about 6.4 million timeline writes per second against 58,000 reads — 110 writes per read, and that's the load the estimate never computes. The hybrid works because the follower distribution is a power law, so removing the top removes most of the mass. But the bigger saving is on the reader side: half the registered users aren't active on a given day, so fan out only to active users and rebuild lapsed feeds on demand. I'd set the threshold by expected cost rather than follower count, and store references rather than values — which is also why the 56 PB storage figure is wrong by two orders of magnitude."

Next: the components and the high-level design.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue