Design a Chat System
Design a real-time chat application like WhatsApp, Slack, or Facebook Messenger.
Related Concepts: WebSocket Connections, Long Polling, Message Queues, Presence Service, Read Receipts, Push Notifications, Message Storage, End-to-End Encryption, Connection Gateway
Step 1: Requirements and Scope
Functional Requirements
- One-on-one messaging
- Group messaging (up to 500 members)
- Online presence indicators
- Read receipts (sent, delivered, read)
- Push notifications for offline users
- Message history with search
Non-Functional Requirements
| Requirement | Target | Rationale |
|---|---|---|
| Latency | < 100ms delivery | Real-time feel |
| Availability | 99.99% | Communication is critical |
| Ordering | Per-conversation | Messages must appear in order |
| Delivery | At-least-once | No lost messages |
| Persistence | Forever (or user-deleted) | Message history matters |
Scale Estimation
- 100 million daily active users
- 50 billion messages per day (~600K/second)
- Average message size: 100 bytes
- Peak: 5x average (1M messages/second)
- Storage: 50B x 100 bytes = 5TB/day
Step 2: Communication Protocols
Protocol Comparison
| Protocol | Connection | Latency | Server Load | Use Case |
|---|---|---|---|---|
| HTTP Polling | New per request | High | Very High | Fallback only |
| Long Polling | Held open | Medium | Medium | Simple real-time |
| WebSocket | Persistent | Low | Low | Chat, gaming |
| Server-Sent Events | Persistent (one-way) | Low | Low | Notifications |
WebSocket (Recommended)
Loading diagram...
| Pros | Cons |
|---|---|
| Full-duplex communication | Stateful (harder to scale) |
| Low latency | Connection management complexity |
| Efficient (no HTTP overhead) | Need fallback for firewalls |
Step 3: High-Level Architecture
Loading diagram...
Step 4: Message Flow
One-on-One Messaging
Loading diagram...
Group Messaging
Loading diagram...
Fan-Out Strategy for Groups
| Group Size | Strategy | Rationale |
|---|---|---|
| Small (< 100) | Fan-out on write | Pre-deliver to all members |
| Large (100-500) | Fan-out on write | Still manageable |
| Very large (500+) | Fan-out on read | Avoid write amplification |
Step 5: Message Storage
Schema Design
| Table | Purpose | Key Structure |
|---|---|---|
messages | Store all messages | (conversation_id, message_id) |
conversations | Conversation metadata | (conversation_id) |
conversation_members | Who is in each conversation | (conversation_id, user_id) |
user_conversations | User's conversation list | (user_id, conversation_id) |
Message Table Structure
| Column | Type | Purpose |
|---|---|---|
message_id | BIGINT | Snowflake ID (time-sorted) |
conversation_id | BIGINT | Groups messages together |
sender_id | BIGINT | Who sent it |
content | TEXT | Message body |
content_type | ENUM | text, image, video, file |
created_at | TIMESTAMP | For display |
status | ENUM | sent, delivered, read |
Database Choice
| Database | Pros | Cons | Best For |
|---|---|---|---|
| Cassandra | High write throughput, linear scaling | Eventual consistency | Message storage |
| PostgreSQL | ACID, familiar | Vertical scaling limits | Metadata, small scale |
| ScyllaDB | Cassandra-compatible, faster | Less mature | High performance |
| TiDB | MySQL-compatible, distributed | Newer | Hybrid workloads |
Recommendation: Cassandra for messages, PostgreSQL for user/conversation metadata
Step 6: Online Presence
Presence Architecture
Loading diagram...
Presence States
| State | Indicator | Storage |
|---|---|---|
| Online | Green dot | user:{id}:status = online, TTL 10s |
| Away | Yellow dot | user:{id}:status = away, TTL 30s |
| Offline | Gray dot | Key expired or deleted |
| Last seen | Timestamp | user:{id}:last_seen = timestamp |
Scaling Presence Updates
| Approach | Description | When to Use |
|---|---|---|
| Eager | Push status to all friends on change | Small friend lists (< 100) |
| Lazy | Send status when friend opens chat | Large friend lists |
| Hybrid | Eager for close friends, lazy for others | Best of both |
Step 7: Read Receipts
Message States
Loading diagram...
Read Receipt Storage
| Field | Type | Purpose |
|---|---|---|
conversation_id | BIGINT | Which conversation |
user_id | BIGINT | Who read |
last_read_message_id | BIGINT | Up to which message |
read_at | TIMESTAMP | When they read |
Efficiency: Last Read Pointer
Instead of tracking each message, store last read position:
| Approach | Storage | Updates | Accuracy |
|---|---|---|---|
| Per-message status | High | Many | Exact |
| Last-read pointer | Low | One per read | Approximate |
Recommendation: Last-read pointer for efficiency
Step 8: Multi-Device Sync
Challenge
User A has phone and laptop both online. Both devices must stay in sync.
Loading diagram...
Sync Protocol
| Event | Action |
|---|---|
| Device connects | Send messages since last_sync_id |
| New message received | Broadcast to all user's devices |
| Message sent | Sync to other devices |
| Device offline | Queue messages, sync on reconnect |
Step 9: Connection Management
Sticky Sessions
WebSocket connections are stateful. Users must connect to the same server.
Loading diagram...
Handling Server Failures
| Scenario | Handling |
|---|---|
| Server crashes | Client reconnects, load balancer routes to new server |
| Graceful shutdown | Server notifies clients, triggers reconnect |
| Network partition | Heartbeat timeout, client reconnects |
Step 10: Push Notifications
Offline Message Delivery
Loading diagram...
Push Notification Content
| Element | Recommendation |
|---|---|
| Title | Sender's name |
| Body | Message preview (truncated) |
| Badge | Unread count |
| Sound | User preference |
| Data | Conversation ID for deep link |
Real-World Systems
| System | Scale | Notable Features |
|---|---|---|
| 2B users | End-to-end encryption, Erlang backend | |
| Slack | 20M DAU | Channels, threads, extensive integrations |
| Discord | 150M MAU | Voice/video, server-based communities |
| Telegram | 800M MAU | Cloud-based, bots, channels |
| Facebook Messenger | 1B+ users | Integrated with Facebook, rich media |
Summary: Key Design Decisions
| Decision | Options | Recommendation |
|---|---|---|
| Protocol | HTTP polling, WebSocket | WebSocket with HTTP fallback |
| Message storage | SQL, NoSQL | Cassandra for messages, PostgreSQL for metadata |
| Group fan-out | On write, On read | On write for groups < 500 |
| Presence | Eager, Lazy | Hybrid based on relationship |
| Read receipts | Per-message, Pointer | Last-read pointer |
| Connection routing | Random, Sticky | Consistent hash for sticky sessions |