Low Level Design: Location Sharing Service

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:

  1. Write row to Location table.
  2. Update Redis key lastloc:{device_id} with the new coordinates (TTL: 7 days).
  3. 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: realtime streams live updates; last-known returns a single snapshot.
  • Expiry: sessions have an expires_at; expired sessions reject new WebSocket connections.
  • Revocation: owner calls DELETE /share/{share_code} → sets revoked_at, closes all active WebSocket connections for that code, removes Redis set.
  • Viewer cap: max_viewers enforced 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: Netflix Interview Guide 2026: Streaming Architecture, Recommendation Systems, and Engineering Excellence

See also: Uber Interview Guide 2026: Dispatch Systems, Geospatial Algorithms, and Marketplace Engineering

See also: Snap Interview Guide

Scroll to Top