Menu
Dev.to #systemdesign·July 23, 2026

Designing a Scalable User Presence System with Redis TTLs

This article details the architectural evolution of a user online/offline presence system, moving from naive database polling to a highly scalable solution leveraging Redis's time-to-live (TTL) functionality. It highlights critical trade-offs in data durability and write amplification when dealing with ephemeral state at scale, ultimately demonstrating how an in-memory store like Redis, combined with native expiry, addresses these challenges effectively.

Read original on Dev.to #systemdesign

The Challenge of Tracking Ephemeral State at Scale

Building a system to track user online/offline presence, a seemingly simple feature, quickly exposes significant scaling challenges. Initial, naive approaches, such as adding a boolean column to a relational database, fail when users don't explicitly log out. This leads to stale data and an inaccurate representation of user status.

Naive Approach: Database Boolean Column

plaintext
flowchart LR
A[User] -->|login| B[Login Service]
B -->|UPDATE users SET is_online=true| C[(Database)]
A -->|logout| B
B -->|UPDATE users SET is_online=false| C

This approach quickly becomes problematic because users rarely log out cleanly. Browser closures, network drops, or device power-offs leave the `is_online` flag set to `true` indefinitely, leading to incorrect presence information.

Improved Approach: Client-Side Heartbeat Polling

A common improvement is to implement client-side heartbeats, where the client periodically pings the server to indicate activity. The server then updates a `last_active_at` timestamp in the database. Users are considered online if their `last_active_at` is within a recent window (e.g., the last minute).

plaintext
sequenceDiagram
  participant Client
  participant Server
  participant DB
  loop every 30 seconds
    Client->>Server: POST /heartbeat
    Server->>DB: UPDATE user_activity SET last_active_at = now()
  end
⚠️

Scaling Challenge: Write Amplification

While solving the staleness issue, this heartbeat mechanism introduces a severe write amplification problem. For 10,000 concurrent users sending heartbeats every 30 seconds, this translates to over 300 writes per second directly to the primary transactional database. This can quickly overwhelm the database and impact its performance for core functionalities.

The Key Insight: Ephemeral Data Doesn't Need Durability

The critical realization for a scalable presence system is that presence data is ephemeral. Losing this state during a system restart is acceptable, as users will send new heartbeats and self-heal the system. This means presence data does not require the durability guarantees of a primary transactional database.

Leveraging Redis with Native TTLs

An in-memory data store like Redis is ideal for this ephemeral state. Instead of storing timestamps and querying with `WHERE` clauses, Redis's native Time-To-Live (TTL) functionality can be used. Each heartbeat updates a key in Redis with an expiry time. If the key exists, the user is online; if it expires, they are offline. Redis handles the expiry automatically and efficiently.

plaintext
flowchart LR
A[User] -->|heartbeat| B[Server]
B -->|SET presence:id EX 30| C[(Redis)]
B -.->|no impact| D[(Primary DB)]
style D stroke-dasharray: 5 5
  • `SET user:123 1 EX 30`: Marks user 123 as online, expiring in 30 seconds.
  • `EXISTS user:123`: Quickly checks if user 123 is online. No timestamp math needed.
ℹ️

Resilience to Redis Outages

Because presence data is disposable, a Redis outage does not result in data loss requiring complex recovery. A new Redis instance can be spun up, and the system will self-heal as clients send subsequent heartbeats, repopulating the presence state.

Optimizing for Fan-Out Reads with Pipelining

While checking a single user's status with `EXISTS` is fast, fetching the status for many users (e.g., 200 friends in a chat list) could still incur multiple network round trips. This is resolved using Redis pipelining, which batches multiple commands into a single round trip, significantly reducing latency and improving efficiency for bulk lookups.

presence systemonline/offline statusRedisTTLheartbeatscalabilityephemeral datasystem design patterns

Comments

Loading comments...