Design Nearby Friends Real-Time Location Service
1. Problem Statement & Scope Clarification
System Mission
Design a planetary-scale, real-time location sharing and proximity alerting platform (equivalent to Apple Find My, Snapchat Snap Map, or Zenly) capable of continuously ingesting live GPS coordinates from tens of millions of concurrent mobile devices, evaluating mutual friendship graphs, filtering spatial proximity (), and delivering real-time map updates with sub-2-second propagation latency while rigorously defending mobile device battery longevity.
At a glance:
| Metric | Value |
|---|---|
| Broadcasters | 10M Active Devices |
| Ingest QPS | 333,333 writes/sec |
| Peak Ingest | 500,000 QPS |
| Latency SLA | < 2.0s End-to-End |
| Availability SLA | 99.999% |
| WebSocket Tasks | 200 Tasks |
| Spatial Grid | H3 Res 7, k-ring 2 |
| Fan-Out | Pruned H3 Pub/Sub (200x) |
| Live State | In-Memory Redis |
Functional Requirements
- Periodic Location Broadcast (
UpdateLocation): Ingest high-frequency GPS coordinates (lat,lon,accuracy,speed,timestamp) every 30 seconds from active mobile clients running in the foreground or authorized background. - Real-Time Nearby Friends Discovery (
GetNearbyFriends): Retrieve a real-time list of mutual friends within a radius, including exact or blurred distance, bearing angle, and last-heartbeat timestamp. - Proximity Push Notifications: Trigger real-time push notifications when a mutual friend crosses a configurable proximity threshold (e.g., enters a geofence).
- Granular Privacy & Ghost Mode Controls: Support per-user and per-friend privacy settings: Precise Location (exact coordinates), Fuzzy / City-Level (randomized within ), and Ghost Mode (completely invisible).
- Battery-Aware Kinematic Polling: Dynamically throttle device GPS hardware polling intervals based on velocity and physical movement states (Stationary, Walking, Driving).
Non-Functional Requirements (SLAs & SLOs)
- High Ingestion Throughput: Ingest an average of with headroom for a peak.
- Propagation Latency: P95 end-to-end delivery of friend location updates across WebSockets , .
- Availability: uptime SLA for the WebSocket Gateway and Ingestion tiers ( unscheduled downtime/year).
- Data Ephemerality: Live GPS coordinates are strictly ephemeral (stored in-memory with a 10-minute TTL). Zero unencrypted raw GPS trails are persisted to permanent storage without explicit user opt-in.
- Battery & Network Efficiency: GPS polling and network transmission must consume battery per hour of active background tracking.
Hexagonal Discretization vs Square Grids: Unlike square grids or Geohashes where diagonal corner distances () distort radial calculations, Uber H3 hexagonal cells feature 6 equidistant immediate neighbors. At Resolution 7 a hexagon is flat-to-flat, so a ring of radius around a user's cell is guaranteed to reach at least from the user in every direction: (7 cells) covers only , which is why the friend radius needs the ring of 19 cells ( guaranteed). The ring shape is what eliminates boundary-crossing edge anomalies.
2. Capacity & Scale Estimation (Back-of-the-Envelope Math)
User Scale & Ingestion QPS
- Daily Active Users (DAU): ().
- Concurrent Active Broadcasters: of DAU active simultaneously = .
- Broadcast Frequency: 1 location update every 30 seconds ().
- Average Ingest QPS:
- Peak Ingest QPS ( diurnal weekend evening spike):
The Quadratic Fan-Out Disaster Proof
Assume an average user has . If every location update is naively broadcast to all friends:
Delivering WebSocket frames per second would require hundreds of gigabits of outbound bandwidth, massive Redis Pub/Sub cluster saturation, and millions of dollars in monthly cloud infrastructure costs.
Mathematical Mitigation via Mutual Cell Pub/Sub Pruning
Instead of indiscriminate global fan-out, we apply a two-stage filter:
- Online Status Filter: Only of a user's friends are online and connected to a WebSocket gateway at any given moment:
- Spatial Proximity Filter: Only of online friends are located in the same or adjacent geographic cell ( away):
This architectural pruning achieves a message volume reduction (from down to ), making real-time delivery completely tractable on a production AWS deployment.
Network Bandwidth Sizing
- Uplink Payload Size (Client Server):
user_id(16B),lat(8B),lon(8B),accuracy(4B),speed(4B),timestamp(8B),seq(4B) (with JSON/Protobuf envelope ). - Downlink Payload Size (Server Client):
friend_id(16B),lat(8B),lon(8B),distance_m(4B),bearing_deg(4B),timestamp(8B) . - Peak Ingress Bandwidth:
- Peak Egress Bandwidth:
In-Memory Live State Sizing (ElastiCache Redis)
- Live Coordinate Hash per Active User:
Key:
loc:usr:<user_id>(32B), Fields:lat(8B),lon(8B),h3(8B),ts(8B) (with Redis hash overhead ).
- Social Graph In-Memory Cache (Redis Sets): Cached friend lists for active DAU (10M users 400 friend UUIDs 16B) .
- Total Redis Footprint: , provisioned across a 6-node ElastiCache Redis Cluster (
cache.r6g.xlargenodes with 26.32 GiB RAM each).
Compute Fleet Sizing (WebSocket Gateway Fleet)
- Peak Concurrent Connections: .
- An optimized Netty / Rust async WebSocket worker container (4 vCPU, 8 GB RAM) reliably handles .
3. AWS-First High-Level Architecture
The architecture comprises three primary planes: 1) L4/L7 WebSocket Gateway Tier, 2) Ingestion, Spatial Discretization & Mutual Filtering Tier, and 3) Hexagonal Cell Redis Pub/Sub Backplane.
Synthesizing vector architecture diagram...
Follow Alice's location update to Bob's screen. Alice's phone sends it over its WebSocket to a gateway, which forwards it to the location router. In the "Location Ingestion & Social Filtering Tier" panel, the router saves her latest coordinates in Redis (with a 10-minute TTL, so stale users disappear) and knows her friend list from the graph cache. In the "Hexagonal Cell Pub/Sub Backplane" panel, it publishes the update to the channel of Alice's H3 cell; each gateway subscribes only to the cells around its connected users, so the update reaches just the gateways that might care. Bob's gateway checks that Alice is his friend, computes the distance, and pushes "Alice is 1.2 km away". Publishing by place instead of to every friend means each update reaches a handful of gateways, not all of them, which keeps the system scalable.
Data Flow Walkthrough
- WebSocket Handshake & Channel Subscription: When Bob opens the mobile app, he establishes a persistent WSS connection via NLB to a WebSocket Gateway task (
WS_Node3). Bob's client sends his initial coordinate(37.7749, -122.4194). The gateway computes Bob's Uber H3 Resolution 7 Cell (e.g.,872830828ffffff) and the ring around it (center + 6 + 12 = 19 cells, the smallest ring that fully contains a radius).WS_Node3subscribes to these 19 Redis Pub/Sub channels on Bob's behalf; because subscriptions are multiplexed per gateway (Section 8.6), nearby users on the same gateway share them. - Location Ingestion Ping: Alice's device broadcasts her location update frame
(lat, lon)over her open WebSocket toWS_Node1.WS_Node1forwards the raw ping to the Location Router service. - Live State Update & Ephemeral TTL: The Location Router writes Alice's coordinates to ElastiCache Redis (
HSET loc:usr_alice lat ... lon ...followed byEXPIRE loc:usr_alice 600in one pipeline;HSEThas noEXoption of its own), resetting her 10-minute TTL heartbeat. - Hexagonal Spatial Publishing: The Location Router converts Alice's coordinate to an H3 Resolution 7 index and publishes the location payload exclusively to that specific cell's Redis Pub/Sub channel:
PUBLISH channel:h3:872830828ffffff { user_id: Alice, lat: ..., lon: ... } - Gateway Mutual Friendship Gating:
WS_Node3receives the message because Bob is subscribed to that H3 cell. Before forwarding the update,WS_Node3evaluates mutual friendship via an atomic in-memory check:SISMEMBER friends:usr_bob usr_alice - Haversine Distance Filter & Push: If mutual friendship is verified,
WS_Node3calculates the exact spherical Haversine distance between Alice and Bob. If ,WS_Node3encodes the downlink frame and pushes it down Bob's open WebSocket connection in . - Proximity Geofence Alert: If Alice crosses from to distance, the Location Router fires an asynchronous event to Amazon SNS, pushing an alert to Bob's notification center ("Alice is nearby!").
Core Request Tracing Execution Walkthrough
| Step # | Event / Action | Component State | Distributed Transition | Output / Response |
|---|---|---|---|---|
| Step 1 | Alice emits binary WebSocket frame with GPS coordinates | Mobile background location thread | NLB terminates TLS; streams frame to ECS WS_Node1 | Frame parsed; JSON payload validated |
| Step 2 | WS_Node1 dispatches location payload to Location Router | In-memory TCP socket stream | Internal gRPC stream from Gateway to Router fleet | Router receives coordinate packet |
| Step 3 | Router computes H3 Resolution 7 index and updates Redis | Location Router CPU memory | HSET loc:usr_alice lat ... lon ... + EXPIRE loc:usr_alice 600 in Redis | Ephemeral coordinates refreshed with 10-min TTL |
| Step 4 | Router publishes payload to H3 cell Pub/Sub channel | Location Router Redis Cluster | PUBLISH channel:h3:872830828ffffff {Alice, lat, lon} | Event broadcast across Redis shard backplane |
| Step 5 | Redis forwards event to gateways subscribed to cell | Redis Pub/Sub engine | TCP packet dispatched to connected WS_Node3 | WS_Node3 event loop triggers message handler |
| Step 6 | WS_Node3 executes mutual friendship check | WS_Node3 local memory / Redis | SISMEMBER friends:usr_bob usr_alice returns TRUE | Alice confirmed as Bob's authorized mutual friend |
| Step 7 | WS_Node3 computes geodesic Haversine distance | Gateway CPU thread | ; qualifies for delivery | |
| Step 8 | WS_Node3 writes downlink frame down Bob's TCP socket | WebSocket connection buffer | WSS frame dispatched over active TLS socket | Bob's map UI renders Alice's updated avatar |
4. API Interface Design & Wire Protocols
1. Client Location Update Frame (WebSocket JSON)
Dispatched by mobile clients every 30 seconds over the persistent WSS connection.
json{ "action": "UPDATE_LOCATION", "coordinates": { "latitude": 37.774929, "longitude": -122.419416, "altitude_meters": 42.5, "accuracy_meters": 8.0, "speed_mps": 1.4, "bearing_degrees": 182.0 }, "motion_state": "WALKING", "client_timestamp_epoch_ms": 1767225600120, "sequence_number": 4820 }
2. Server Downlink Location Broadcast Frame (WebSocket JSON)
Delivered from the gateway to connected mutual friends in the same spatial cell.
json{ "event": "FRIEND_LOCATION_UPDATE", "friend_id": "usr_alice_99a8b7c6", "coordinates": { "latitude": 37.778291, "longitude": -122.418294 }, "distance_meters": 384, "bearing_degrees": 24.5, "privacy_mode": "PRECISE", "last_updated_epoch_ms": 1767225600150 }
3. Update Privacy Mode (PUT /v1/users/me/privacy)
Configures user location visibility and obfuscation.
httpPUT /v1/users/me/privacy HTTP/1.1 Host: api.nearby.platform.aws.internal Authorization: Bearer jwt_usr_alice_99a8b7c6 Content-Type: application/json { "global_privacy_mode": "FUZZY_CITY", "ghost_mode_until_epoch_ms": null, "friend_exceptions": [ { "friend_id": "usr_bob_77182930", "privacy_mode": "PRECISE" } ] }
- Modes:
PRECISE: Exact coordinates broadcast to mutual friends.FUZZY_CITY: Coordinates obfuscated with randomized Gaussian noise () and snapped to nearest landmark.GHOST: Completely invisible. All live Redis keys immediately deleted.
Status Codes & Error Contracts
200 OK: Successful privacy update or REST snapshot retrieval.400 Bad Request: Out-of-bounds coordinates or invalid motion state string.401 Unauthorized: Missing or expired WebSocket handshake JWT.429 Too Many Requests: Client broadcasting faster than once every 10 seconds.
5. Data Models & Storage Architecture
Database Selection Justification
- Amazon ElastiCache Redis 7 Cluster: Master datastore for active ephemeral locations, H3 cell channels, and active friendship cache. Redis delivers sub-millisecond in-memory read/write performance, native key expiration (10-minute TTL for dead connections), and horizontal pub/sub sharding.
- Amazon DynamoDB: Maintains optional 30-day location history trails for opt-in auditing. DynamoDB handles 500k writes/sec with automated TTL purging and zero operational management.
- Amazon Aurora PostgreSQL Multi-AZ: Master persistence for permanent user profiles, mutual friendship graph edges, and privacy settings.
1. Redis In-Memory Geospatial Data Structures
text1. Live Ephemeral Coordinate Hash: Key: loc:usr:<user_id> Type: HASH Fields: { "lat": "37.774929", "lon": "-122.419416", "h3": "872830828ffffff", "ts": "1767225600120" } TTL: 600 seconds (10 Minutes) 2. Mutual Friend Graph Set: Key: friends:usr:<user_id> Type: SET Value: [ "usr_bob_101", "usr_charlie_102", "usr_david_103", ... ] TTL: 86400 seconds (Refreshed on friendship mutations) 3. H3 Hexagonal Cell Pub/Sub Channel: Channel: channel:h3:<h3_resolution_7_index> Example: channel:h3:872830828ffffff Payload: { "user_id": "usr_alice", "lat": 37.7749, "lon": -122.4194, "ts": 1767225600120 }
2. DynamoDB Location History Schema (LocationHistoryTable)
Partition Key (PK) | Sort Key (SK) | Attributes | Description |
|---|---|---|---|
USER#<user_id> | TS#<epoch_ms> | lat, lon, accuracy, speed, motion_state, ttl=1769817600 | Chronological breadcrumb trail |
Unlock Complete Architecture & Production Runbooks
You have explored the free architectural preview (~35%). Spend 1 Coin to unlock the remaining 6 production deep-dive sections for a full 24 hours.