PrepZone Logo
PrepZone

Nearby Friends Location

Continuous location sharing with privacy controls and efficient geospatial queries.

Read these first

Why this matters

  • Snapchat Map, Find My, and StreamHub's "friends watching nearby" share the same pattern: frequent writes, infrequent reads, strict privacy boundaries.
  • Unlike ride-hailing (millions of strangers), friend graphs are sparse — you query locations for 50–500 friends, not the entire user base.
  • Battery drain from constant GPS polling is a product killer; adaptive update intervals are a design requirement, not an optimisation.

Nearby Friends components

  • Location publisher — client sends lat/lng on interval (adaptive: 30s stationary, 5s moving).
  • Friend graph service — bidirectional friendship with privacy settings (share with all, select friends, ghost mode).
  • Location store — per-user latest position with TTL; Redis GEO or geohash-indexed KV.
  • Query service — given user A, fetch friend IDs, batch-lookup positions, filter by radius and privacy.
  • Push notifier — alert when a friend enters your radius ("Alex is 500m away").

Architecture overview

Proximity / ride-matching

WebSocketCLIENT
Driver appGPS every 4s
INTEGRATION
Kinesislocation stream
DATABASE
ElastiCache G…GEORADIUS
COMPUTE
Match APIEKS
CLIENT
Rider appWS assigned
Driver GPS → Kinesis → Redis GEO → match API → WebSocket to rider.
Java
PUT /v1/location
Authorization: Bearer <token>
Content-Type: application/json

{
  "lat": 37.7749,
  "lng": -122.4194,
  "accuracy_m": 12,
  "timestamp_ms": 1740847200000
}
Java
GET /v1/friends/nearby?radius_m=1000
Authorization: Bearer <token>

→ 200
[
  { "user_id": "usr_42", "lat": 37.7751, "lng": -122.4188, "distance_m": 320, "updated_at": "..." },
  { "user_id": "usr_99", "lat": 37.7730, "lng": -122.4210, "distance_m": 890, "updated_at": "..." }
]

Privacy model

AspectSettingBehaviour
Ghost modeUser invisible to everyoneLocation not stored or published
Select friendsWhitelist of user IDsOnly listed friends see location
Approximate locationSnap to 1 km gridHides exact address while showing area
Time-limited sharingAuto-expire after 8 hoursReduces forgotten-sharing risk
  • Ghost mode

    SettingUser invisible to everyone
    BehaviourLocation not stored or published
  • Select friends

    SettingWhitelist of user IDs
    BehaviourOnly listed friends see location
  • Approximate location

    SettingSnap to 1 km grid
    BehaviourHides exact address while showing area
  • Time-limited sharing

    SettingAuto-expire after 8 hours
    BehaviourReduces forgotten-sharing risk

Privacy checks happen server-side on every query — never trust the client to filter friends.

Efficient friend-based lookup

Unlike Uber's global spatial index, Nearby Friends inverts the query: start from the friend list, then fetch locations.

Java
def get_nearby_friends(user_id: str, radius_m: int) -> list:
    friend_ids = friend_graph.get_friends(user_id)
    user_location = location_store.get(user_id)
    results = []

    for friend_id in friend_ids:
        if not privacy_allows(friend_id, user_id):
            continue
        friend_loc = location_store.get(friend_id)
        if friend_loc is None or is_stale(friend_loc, max_age_s=120):
            continue
        dist = haversine(user_location, friend_loc)
        if dist <= radius_m:
            results.append({**friend_loc, "distance_m": dist})

    return sorted(results, key=lambda r: r["distance_m"])

Adaptive update intervals

Java
{
  "update_policy": {
    "stationary_interval_s": 60,
    "moving_interval_s": 10,
    "speed_threshold_mps": 1.5,
    "significant_change_m": 50
  }
}

Clients increase interval when stationary (saving battery) and decrease when moving. Server-side geofencing can skip writes when position change is below the significance threshold.

Geofence notifications

When friend B enters a radius around friend A, push a notification.

Java
{
  "event": "friend.entered_radius",
  "viewer_id": "usr_01",
  "friend_id": "usr_42",
  "distance_m": 480,
  "timestamp": "2026-04-01T16:00:00Z"
}

Evaluate geofence crossings in a streaming processor (Kafka + Flink) that maintains last-known positions and friend-pair subscriptions.

Scale estimates

MetricEstimate
Users with location sharing enabled20M
Average friends per user150
Location updates per user per hour~120 (adaptive)
Write QPS20M × 120 / 3600 ≈ 670K
Nearby query QPS~50K peak
Storage per user~200 bytes (latest position only)
  • Users with location sharing enabled

    Estimate20M
  • Average friends per user

    Estimate150
  • Location updates per user per hour

    Estimate~120 (adaptive)
  • Write QPS

    Estimate20M × 120 / 3600 ≈ 670K
  • Nearby query QPS

    Estimate~50K peak
  • Storage per user

    Estimate~200 bytes (latest position only)

Friend-list lookup is O(friends), not O(users). 150 Redis GETs per query is fast with pipelining.

Data retention and compliance

Location data is sensitive PII. Apply strict retention policies.

Compliance requirements

  • TTL on location records — auto-delete positions older than 24 hours unless user opts into history.
  • Audit log — record who queried whose location and when, for abuse investigations.
  • GDPR/CCPA deletion — cascade delete location data when a user deletes their account.
  • Encryption — TLS in transit; encrypt at rest in the location store.

Quick recall

Everything you need if you only revisit this box.

  • Nearby Friends = friend-graph-first lookup, not global spatial scan.
  • Privacy controls (ghost mode, select friends, approximate location) enforced server-side.
  • Adaptive GPS intervals save battery: longer when stationary, shorter when moving.
  • Batch-fetch friend locations with pipelined Redis MGET for sub-20 ms queries.
  • Strict TTL, audit logs, and encryption — location is sensitive PII.

Test yourself

Answer these before moving on — recall is what makes it stick.