Location Sharing Service: Low Level Design
Data Model
Location Table (Time-Series)
Location
--------
device_id BIGINT
lat DOUBLE PRECISION
lng DOUBLE PRECISION
accuracy_meters FLOAT
heading FLOAT -- degrees 0-360
speed FLOAT -- m/s
timestamp TIMESTAMPTZ
PRIMARY KEY (device_id, timestamp)
PARTITION BY RANGE (timestamp) -- one partition per day
Raw location rows are retained for 24 hours, then dropped with the partition. The last-known location for each device is maintained separately in Redis for instant retrieval.
Share Session Table
ShareSession
------------
id BIGINT PK
owner_device_id BIGINT
share_code VARCHAR(8) UNIQUE -- random token, e.g. "aB3xQr7Z"
permission ENUM('realtime','last-known')
expires_at TIMESTAMPTZ
max_viewers INT
created_at TIMESTAMPTZ
revoked_at TIMESTAMPTZ
Live Location Updates
Ingestion API
POST /locations
Authorization: Bearer {device_token}
{
"lat": 37.7749,
"lng": -122.4194,
"accuracy_meters": 5.0,
"heading": 270.0,
"speed": 12.5,
"timestamp": "2024-01-01T12:00:00Z"
}
Devices POST every 5 seconds while moving, or every 30 seconds while stationary (battery optimization via adaptive frequency — determined client-side using accelerometer data).
On receipt:
- Write row to
Locationtable. - Update Redis key
lastloc:{device_id}with the new coordinates (TTL: 7 days). - PUBLISH to Redis channel
share:{share_code}for all active share sessions owned by this device.
Watcher Registry and WebSocket Fan-Out
Watcher Registry
Redis Set key: watchers:{share_code}
Members: {node_id}:{connection_id}
TTL: reset on each WebSocket heartbeat
When a viewer opens a WebSocket connection using a share code, their connection ID is added to the set. Multiple app server nodes each subscribe to the Redis Pub/Sub channel for every active share code they serve.
Fan-Out Flow
Device POST /locations
│
▼
Write to DB + update Redis lastloc
│
▼
PUBLISH to Redis channel "share:{share_code}"
│
├──▶ Node A receives → pushes to connected WebSocket clients
└──▶ Node B receives → pushes to connected WebSocket clients
Privacy and Access Control
- Share code: random 8-character alphanumeric token; not guessable. Shared out-of-band (SMS, link).
- Permission levels:
realtimestreams live updates;last-knownreturns a single snapshot. - Expiry: sessions have an
expires_at; expired sessions reject new WebSocket connections. - Revocation: owner calls
DELETE /share/{share_code}→ setsrevoked_at, closes all active WebSocket connections for that code, removes Redis set. - Viewer cap:
max_viewersenforced at WebSocket handshake time via set cardinality check.
REST API Summary
POST /locations -- ingest location update
POST /share -- create share session → returns share_code
GET /share/{share_code}/location -- get last-known location (HTTP)
WS /share/{share_code}/live -- real-time WebSocket stream
DELETE /share/{share_code} -- revoke share session
Scalability Notes
- Redis Pub/Sub fan-out decouples ingestion from delivery; any node can serve any viewer.
- Time-series partitioning keeps the hot partition small; old partitions are dropped cheaply.
- WebSocket nodes are stateless beyond the in-memory connection map; horizontal scaling is straightforward.
- For very high fan-out (live event sharing with thousands of viewers), Redis Streams with consumer groups can replace simple Pub/Sub.
{
“@context”: “https://schema.org”,
“@type”: “FAQPage”,
“mainEntity”: [
{
“@type”: “Question”,
“name”: “How does a real-time location sharing service fan out updates to multiple viewers?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “The device POSTs a location update to the ingestion service, which writes it to the database and then PUBLISHes to a Redis Pub/Sub channel keyed by the share code. All WebSocket server nodes subscribed to that channel receive the message and push it to their connected viewer clients.”
}
},
{
“@type”: “Question”,
“name”: “How do you implement privacy controls in a location sharing service?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Generate a random 8-character share code (not guessable) as the access token. Support expiry timestamps and a max_viewers cap enforced at WebSocket handshake time. Allow the owner to revoke a session at any time, which closes all active connections and removes the watcher registry from Redis.”
}
},
{
“@type”: “Question”,
“name”: “How do you handle location data retention to protect user privacy?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Partition the Location table by day and drop old partitions after 24 hours. Keep only the last-known location in Redis with a 7-day TTL. This ensures raw location history is not retained long-term while still supporting 'last-known' permission mode.”
}
},
{
“@type”: “Question”,
“name”: “How does battery optimization work in a location sharing app?”,
“acceptedAnswer”: {
“@type”: “Answer”,
“text”: “Use adaptive update frequency determined client-side: POST location every 5 seconds while the device is moving (detected via accelerometer or GPS speed), and reduce to every 30 seconds while stationary. This significantly reduces battery drain and network usage without meaningfully degrading the sharing experience.”
}
}
]
}
See also: Uber Interview Guide 2026: Dispatch Systems, Geospatial Algorithms, and Marketplace Engineering
See also: Snap Interview Guide