Fan-Out Strategies: Write-Time vs Read-Time
User sends a message to a group of 500 people. Do you write that message into 500 inboxes right now? Or store it once and let each person fetch it when they open the app?
This is the fan-out problem. And the answer changes everything about your storage, latency, and infrastructure costs.
Fan-Out on Write (Push)#
When a message arrives, immediately write it to every recipient’s inbox. Reads become trivial: just query your own inbox.
public void sendGroupMessage(String groupId, Message msg) {
List<String> members = groupService.getMembers(groupId);
for (String memberId : members) {
inboxRepo.save(new InboxEntry(memberId, msg));
}
}
Writes are expensive (500 members = 500 writes), but reads are fast. Every user’s inbox is pre-computed. Great when you have way more reads than writes.
The problem? One user with 100K followers posts an update. That’s 100K writes. Your write throughput just spiked, and backpressure becomes critical.
Fan-Out on Read (Pull)#
Store the message once. When a user opens the app, query all sources they follow and merge the results.
public List<Message> getTimeline(String userId) {
List<String> following = followService.getFollowing(userId);
return messageRepo.findRecentByAuthors(following, PageRequest.of(0, 50));
}
Writes are cheap (store once), but reads are expensive. Every read queries multiple sources and merges in real-time. Fine for small follow lists. Painful when someone follows 1,000 sources.
The Hybrid Approach#
Most real systems do both. Fan-out on write for normal users (small groups, reasonable follower counts). Fan-out on read for “hot” users with massive audiences. This is essentially CQRS in practice: different write and read paths optimized for different access patterns.
At Salesforce, we used a similar hybrid for notifications. Standard config changes fanned out on write to affected services. But bulk operations (touching 4000+ service configurations) used a pull model instead. Without that split, bulk updates would have overwhelmed the write path completely.
What I’m Learning#
Fan-out is ultimately a question of where you want to pay the cost: at write time or read time. Neither is universally better. The answer depends on your read/write ratio and whether your tail latency budget can absorb the fan-out spike.
The hybrid approach feels like over-engineering until you hit the user with 100K followers. Then it’s the only thing that works.
Which side do you optimize for, reads or writes?