Skip to main content
PRISM
Loading

The whole walkthrough, written out

Every step below is the same text the guided version shows, in order. The simulations are what make it worth doing rather than reading — but the reasoning is here either way.

1. The question behind the question

Requirements

A feed is a ranking problem wearing a scaling problem, and interviewers rarely want the ranking. What they want is the read/write shape: a feed is read constantly and written rarely, and every post has to reach everyone who follows the author.

Ask whether the feed must be strictly chronological (it makes caching far easier if not), whether it must be complete (it does not — nobody notices a missing post from six hours ago), and how quickly a new post must appear (seconds is fine; milliseconds is not required and costs enormously).

The constraint that decides the architecture is the follower distribution. The average is 200. The maximum is fifty million, and it is the maximum that will break you.

Average 200 followers, maximum 50 million. The average is not the design input.

2. Both directions

Estimate

Fan-out on read: 100 million users loading a feed a few times a day is around 5,000 feed loads a second average, 15,000 at peak. Each one queries posts from 200 accounts. That is three million post queries a second, and no database does that.

Fan-out on write: 100 million users posting twice a day is 200 million posts, each written to 200 follower feeds — 40 billion feed writes a day, about 460,000 a second. Large, but writes are cheap and can be batched, and the read then becomes a single lookup.

So fan-out on write wins by two orders of magnitude on the read path, and that is why every large feed does it. Then somebody with fifty million followers posts, and one write becomes fifty million.

Fan-out on read: 3M queries/s. Fan-out on write: 460k writes/s. Neither number is small.

3. The naive design: fan-out on read

A first design

A feed service that, on every load, queries the post store for every account the user follows and merges the results. It is simple, always current, and needs no background jobs.

It is also the design that dies first, and running it shows you why in about ten seconds.

Each feed load touches 200 accounts. What does that do to the p99?

The backend p99 becomes the user median — Fanning out to 200 backends and waiting for all of them means the request is as slow as the slowest of 200 draws. One backend in a hundred being slow is a near-certainty across 200 of them, so the rare case becomes the common one.

4. The tail eats the feed

Watch it break

This is tail latency amplification, and it is worth seeing on its own. Set the fan-out to 200 and watch the user p50 climb toward the backend p99. The backend is not slow — its median is 20ms — and the user is waiting a quarter of a second.

The 200ms budget is gone before ranking, before rendering, before the network hop to the phone. And every one of those 200 queries is real load on the post store, which is now taking three million requests a second.

At a fan-out of 200, the backend p99 is the user median.

5. Move the work to write time

Find the bottleneck

The read is expensive because it does the merging. Do the merging when the post is created instead: write each post into every follower's precomputed feed, and a feed load becomes a single lookup of a single list.

That trades an expensive read for many cheap writes, which is a good trade when reads outnumber writes — and it also makes the feed trivially cacheable, because it is now one key per user.

The cost lands on the write path and on storage: 40 billion feed entries a day, and a post from a large account costs proportionally to its follower count.

Precompute at write time; the read becomes one lookup.

6. Fan-out on write, with a cache

Change one thing

The feed service reads a precomputed list from a cache, falling through to the feed store on a miss. The post service writes into follower feeds asynchronously through a queue, so a slow fan-out never slows a post.

Run it. The read path is now a single cache lookup and the p99 is a few milliseconds. The interesting load has moved entirely to the worker pool draining the fan-out queue, which is exactly where you can afford it: a queue can absorb a burst, and nobody is waiting on it.

The read path is one cache lookup. The cost has moved to a queue nobody waits on.

7. The celebrity problem, and the answer that is both

What gets asked next

Now the question every interviewer asks: what happens when an account with fifty million followers posts? Fan-out on write means fifty million feed writes for one action. At 460,000 writes a second of normal capacity, that single post is a hundred seconds of the entire system's write budget.

The answer is a hybrid, and being able to say it precisely is the point of the whole question. Fan out on write for ordinary accounts, and do NOT fan out for accounts above a threshold. For those, the feed service merges the handful of large accounts a user follows at read time, on top of the precomputed feed.

That works because the distribution is the thing that broke it: almost nobody has fifty million followers, so almost nobody is in the read-time merge, and the merge is over a handful of accounts rather than two hundred. The skew that caused the problem is the same skew that makes the fix cheap.