Presence and online indicators
Heartbeat TTL maps that stay honest under disconnects and clock skew.
Presence and online indicators
Business constraint
Product wants a green dot when a user is "online". Support, chat, and gaming all treat this as a soft signal — but sales dashboards treat missing dots as outages. You must bound false positives and keep write load cheap.
Naive v0
Store last_seen in a primary DB row per user. Every heartbeat UPDATEs the row. UI polls every 5s. Works on a laptop demo with 20 users.
Failure drill
At 200k concurrent users with 15s heartbeats you issue ~13k writes/s to the primary. Disconnects leave dots green for minutes. Two tabs fight. A mobile radio blip looks like logout. Support tickets spike when the DB is slow — presence becomes a canary for unrelated load.
Evolution path
Iteration 1
Move heartbeats to an in-memory TTL map (or Redis) keyed by user id. Expire after ttl = heartbeat_interval * miss_budget. UI reads from the TTL store, not the OLTP primary.
Iteration 2
Separate session presence from device presence. Aggregate devices → user. Soft TTL: serve last-known briefly while a refresh is in flight so UI does not flicker on a single missed beat.
Iteration 3
Shard the presence map by user-id hash. Cap write fan-out with coalescing: multiple heartbeats in a window collapse to one write. Measure false-online rate under simulated disconnect storms.
Implementation cut
Ship a process-local map with monotonic expiry first (lab presence). Document that multi-node presence needs sticky routing or a shared TTL store. Never let presence writes share a transaction pool with checkout.
Numbers
Target: heartbeat write path <1% of DB IOPS. False-online under 30s disconnect <5% of sessions. p99 presence read <5ms from TTL store.
Pattern tags
Self-check
- Why is polling the primary a bad default for presence?
- What does miss_budget buy you?
- How do multi-device sessions change the model?
- What metric catches false-online?
- When would sticky sessions help presence reads?
Walkthrough
Recordings will appear here when published. Until then, work the failure drill and lab locally — pause after each iteration and write the metric that moved.
Lab package: see /systems/labs/presence/.