When your location changes, every friend who has you in their Nearby Friends list needs to know. At 10 million active users each with 200 friends, a single location update could trigger 200 downstream notifications. At 333,000 updates per second, that’s 66 million fan-out operations per second. This is location fan-out: propagating position changes to all interested subscribers efficiently.

Why This Is Different From Chat Fan-Out#

Chat fan-out sends one message to N subscribers. Location fan-out has an extra constraint: most updates are not interesting. If a friend moves 10 meters, you don’t need to know. If their bucket changes (they crossed from “within half a mile” to “within 1 mile”), that’s worth propagating. Filtering at the source reduces fan-out volume by 80-90%.

Apply delta filtering: only fan out when the geohash cell changes, not on every GPS coordinate change. A user walking around within a single geohash cell at precision 5 (5km x 5km) produces zero fan-out events. Crossing a cell boundary produces one.

graph TD A[Location update received] --> B{Geohash cell changed?} B --> |No| C[Discard: no fan-out needed] B --> |Yes| D[Fetch friend list from social graph] D --> E[Filter: friends with location sharing enabled] E --> F[Fan-out: publish to each friend's location channel] F --> G[Friends with open WebSocket connections receive update] F --> H[Offline friends: skip, stale on next query] style A fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style B fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style C fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style D fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style E fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style F fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style G fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff style H fill:#000000,stroke:#00ff00,stroke-width:2px,color:#fff

Pull vs Push#

Two models for delivering location updates:

Push: server sends updates over a persistent WebSocket connection when a friend’s location changes. Low latency, but requires maintaining open connections for all active users. At 10 million users, that’s 10 million open sockets.

Pull: client polls every 30 seconds. Higher latency (up to 30 seconds stale), but no persistent connection overhead. Simpler to scale horizontally.

Most production Nearby Friends features use pull for the general case and push only for users who are “very nearby” (under 0.5 miles). When a friend enters the close range, upgrade to push. When they leave, downgrade to poll. This dramatically reduces the persistent connection count.

Fan-Out Workers#

Fan-out is asynchronous. The location update write path acknowledges immediately; the fan-out happens via a background worker queue. Location update → Redis write → publish to fan-out queue → workers consume and deliver to friend channels.

This decouples the write latency from the fan-out volume. A user with 5,000 friends doesn’t make the location update API slow. Workers process the fan-out at their own pace.

At Oracle#

We had an event fan-out problem for configuration change notifications: when a config changed, all services using that config needed to be notified. With 200+ microservices and config changes happening dozens of times per day, naive fan-out was noisy. We added a filter: services only received notifications for config keys they had explicitly subscribed to. Notification volume dropped 95%. Same principle as location delta filtering.

What I’m Learning#

Fan-out problems always have a filtering optimization. For location, it’s cell-boundary changes only. For chat, it’s active sessions only. Identifying the filter that eliminates unnecessary work is usually the key design decision.

Have you implemented fan-out at scale, and what was the most effective filtering strategy you found?