| Users | Messages/Day | Server Load | Database Load | Network | Notes |
|---|---|---|---|---|---|
| 100 | 1,000 | Single app server handles all | Single DB instance handles writes/reads | Low bandwidth, no CDN needed | Simple setup, no caching needed |
| 10,000 | 100,000 | Multiple app servers behind load balancer | DB starts to see high write/read load | Moderate bandwidth, consider caching | Introduce Redis cache for recent messages |
| 1,000,000 | 10,000,000 | Hundreds of app servers, autoscaling | DB bottleneck: read replicas, sharding needed | High bandwidth, CDN for media files | Use message queues for delivery, partition users |
| 100,000,000 | 1,000,000,000 | Thousands of app servers, geo-distributed | Multi-region DB clusters, advanced sharding | Very high bandwidth, global CDN | Strong consistency challenges, eventual consistency for some data |
One-to-one messaging in HLD - Scalability & System Analysis
Start learning this pattern below
Jump into concepts and practice - no test required
At small scale, the database is the first bottleneck because it must handle all message writes and reads. As users grow, the DB CPU and disk I/O get saturated. This slows down message delivery and retrieval.
- Horizontal scaling: Add more app servers behind a load balancer to handle concurrent connections.
- Database read replicas: Offload read queries to replicas to reduce load on primary DB.
- Sharding: Split user data across multiple database instances by user ID to distribute load.
- Caching: Use Redis or Memcached to cache recent messages and user presence info.
- Message queues: Use queues like Kafka or RabbitMQ to decouple message ingestion and delivery.
- CDN: For media files (images, videos), use CDN to reduce bandwidth on origin servers.
- Geo-distribution: Deploy servers and databases closer to users to reduce latency.
Assuming 1 million users sending 10 messages/day each:
- Messages per second (QPS): ~115 (10M messages / 86400 seconds)
- Database writes: 115 QPS (each message is a write)
- Database reads: 230 QPS (assuming 2 reads per message for delivery and retrieval)
- Storage: 10M messages/day * 1KB/message = ~10GB/day
- Network bandwidth: 10M messages/day * 1KB = ~120 KB/s peak
- One DB instance can handle ~5,000 QPS, so single DB can handle this load but with little room for growth.
Start by explaining the user scale and expected message volume. Identify the database as the first bottleneck. Discuss horizontal scaling of app servers, caching, and database read replicas. Then explain sharding and geo-distribution for large scale. Always justify why each solution fits the bottleneck.
Your database handles 1000 QPS. Traffic grows 10x to 10,000 QPS. What do you do first?
Answer: Add read replicas to offload read queries and reduce load on the primary database. Also consider caching frequently accessed data to reduce DB hits.
Practice
Solution
Step 1: Understand message flow in one-to-one messaging
Messages are sent from one user to another through a server acting as an intermediary.Step 2: Identify server's role
The server receives messages from the sender and forwards them to the intended receiver, ensuring delivery and possibly storing temporarily.Final Answer:
To receive messages from the sender and deliver them to the receiver -> Option CQuick Check:
Server acts as message relay = B [OK]
- Thinking users connect directly without server
- Assuming server only stores without forwarding
- Confusing one-to-one with broadcast messaging
Solution
Step 1: Identify privacy needs in messaging
Privacy means only sender and receiver can read the message content.Step 2: Match privacy solution
End-to-end encryption encrypts messages so only sender and receiver can decrypt them, ensuring privacy.Final Answer:
End-to-end encryption -> Option AQuick Check:
Privacy needs encryption = C [OK]
- Confusing load balancer as privacy tool
- Thinking caching secures messages
- Assuming message queue provides privacy
def send_message(sender, receiver, message):
server.store_message(sender, receiver, message)
server.deliver_message(receiver)
send_message('Alice', 'Bob', 'Hello')
What is the expected output or result of this code?Solution
Step 1: Analyze function calls
The function stores the message from Alice to Bob, then delivers it to Bob.Step 2: Check message flow correctness
Message is stored correctly and delivery is triggered for Bob, the intended receiver.Final Answer:
Message 'Hello' is stored and delivered to Bob -> Option DQuick Check:
Store then deliver to receiver = D [OK]
- Confusing sender and receiver in delivery
- Assuming message is not delivered after storing
- Thinking code has syntax errors
def deliver_message(receiver, message):
if receiver.is_online == False:
return
send_to_client(receiver, message)
What is the main issue causing message delivery failure?Solution
Step 1: Understand delivery condition
The function returns early if the receiver is offline, skipping delivery.Step 2: Identify missing handling
Messages are not queued or stored for later delivery, so offline users never get messages.Final Answer:
Messages are dropped if receiver is offline without queuing -> Option BQuick Check:
Offline receiver drops messages = A [OK]
- Thinking syntax error causes failure
- Assuming wrong user receives message
- Assuming offline messages are automatically queued
Solution
Step 1: Understand offline delivery challenge
Receivers may be offline, so messages must be stored to avoid loss.Step 2: Choose scalable solution
Message queues store undelivered messages and retry delivery when receiver reconnects, ensuring reliability and scalability.Final Answer:
Use a message queue to store undelivered messages and retry delivery when receiver is online -> Option AQuick Check:
Queue undelivered messages for offline users = A [OK]
- Dropping messages loses data
- Broadcast wastes resources and breaks privacy
- Requiring both online limits usability
