#39 HyperLogLog/Redis Counts
Concurrent View Counter
Show "37 people are looking at this hotel right now" for millions of pages, cheaply and roughly right.
What is being asked
Count how many distinct people are currently viewing each item. The number changes constantly, exactness does not matter, and there are millions of items.
Requirements
Functional
- Record that a user is viewing an item.
- Return the number of distinct current viewers of an item.
Non-functional
- Very high event rate.
- "Current" means the last few minutes.
- Approximate is fine; memory per item must be tiny.
Design
- While a page is open, the client sends a small heartbeat every 30 seconds with the item id and a visitor id.
- The counter service adds the visitor to a structure keyed by item and minute, with a time to live slightly longer than the window.
- The count is the number of distinct visitors across the last few minute-buckets.
- The page reads the count from a short cache.
Key decisions and trade-offs
- Sets or HyperLogLog. A set per item is exact but grows with viewers. A HyperLogLog counts distinct values in about 12 KB regardless of size, with roughly 1% error, and several can be merged. Use plain sets for small counts and HyperLogLog for large ones.
- Minute buckets with expiry make "current" automatic: old buckets disappear and no clean-up job is needed.
- Heartbeats, not just page loads. Without them a tab opened an hour ago is still counted, or a viewer is forgotten after a minute.
- Do not query per request. Cache each item's count for a few seconds; the number does not need to change faster than a person can read.
Follow-up questions
- How would you avoid counting one person on two devices twice?
- How do you stop the number being inflated by automated traffic?
- What if the product wants "seen 1,240 times today" as well?
- How much memory does this use for ten million items?