| Users | Commands (Writes) | Queries (Reads) | Data Storage | Latency | Complexity |
|---|---|---|---|---|---|
| 100 users | Low volume, single write DB | Low volume, single read DB | Single DB instance | Low latency, simple sync | Simple CQRS setup |
| 10,000 users | Moderate writes, DB scaling needed | High reads, read DB replicas | Primary + read replicas | Eventual consistency visible | Separate read/write DBs |
| 1 million users | High writes, sharded command DB | Very high reads, distributed read DB | Sharding + replication | Increased eventual consistency delay | Event sourcing may be added |
| 100 million users | Massive writes, multi-region shards | Extreme reads, global caches/CDNs | Geo-distributed DB clusters | Consistency trade-offs, async sync | Complex event sourcing + CQRS |
CQRS (Command Query Responsibility Segregation) in HLD - Scalability & System Analysis
Start learning this pattern below
Jump into concepts and practice - no test required
At small scale, the database handling writes (command side) is the first bottleneck because all changes must be processed and stored reliably. As users grow, the write DB CPU and disk I/O limits are reached first.
Read side scales easier with replicas, so write DB capacity is the main limit initially.
- Vertical scaling: Increase CPU, RAM, and disk speed on command DB server to handle more writes.
- Horizontal scaling: Shard the command database by user or entity to distribute writes across servers.
- Read replicas: Add multiple read-only replicas to serve queries and reduce load on primary DB.
- Caching: Use in-memory caches (e.g., Redis) on query side to speed up frequent reads.
- Event sourcing: Store changes as events to enable replay and rebuild read models asynchronously.
- Asynchronous sync: Decouple command and query sides with message queues to improve write throughput.
- Geo-distribution: Deploy DB clusters in multiple regions to reduce latency for global users.
Assuming 1 million users with 10% active concurrently:
- Concurrent users: 100,000
- Writes per second (commands): ~5,000 QPS (assuming 5% write rate)
- Reads per second (queries): ~95,000 QPS (assuming 95% read rate)
- Storage: Command DB stores events/transactions, estimated 1TB/month
- Read DB stores denormalized views, estimated 2TB for fast queries
- Network bandwidth: Reads dominate, ~1 Gbps needed for query responses
Start by explaining the separation of commands and queries and why it helps scalability.
Discuss bottlenecks on the write side first, then read side.
Outline scaling steps: vertical scaling, read replicas, sharding, caching, and asynchronous event processing.
Use real numbers to show understanding of limits and solutions.
Your database handles 1000 QPS writes. Traffic grows 10x to 10,000 QPS writes. What do you do first?
Answer: Implement sharding of the command database to distribute write load across multiple servers, because vertical scaling alone likely won't handle 10x increase efficiently.
Practice
CQRS in system design?Solution
Step 1: Understand CQRS concept
CQRS stands for Command Query Responsibility Segregation, which means separating commands (writes) from queries (reads).Step 2: Identify the main benefit
This separation allows each part to be optimized and scaled independently, improving performance and maintainability.Final Answer:
To separate read and write operations for better scalability -> Option CQuick Check:
CQRS = Separate reads and writes [OK]
- Thinking CQRS combines operations into one service
- Confusing CQRS with security encryption
- Assuming CQRS reduces server count directly
Solution
Step 1: Define Command role in CQRS
Commands are responsible for write operations that modify the system's state.Step 2: Differentiate from Query
Queries only read data without changing it, so they are not commands.Final Answer:
It processes write operations that change system state -> Option AQuick Check:
Command = Write operations [OK]
- Confusing commands with queries
- Thinking commands handle caching
- Assuming commands manage security
Solution
Step 1: Understand asynchronous update in CQRS
In CQRS, the read side is often updated asynchronously via events after the write completes.Step 2: Identify read side behavior after write
Because of this delay, the read side may temporarily show stale data until it receives the update event.Final Answer:
The read side may show the old email briefly due to asynchronous update -> Option DQuick Check:
Read side updates asynchronously = possible stale data [OK]
- Assuming immediate read consistency
- Thinking reads block until writes finish
- Believing read data is deleted during update
Solution
Step 1: Identify how read model updates in CQRS
The read model updates via events sent by the command handler after state changes.Step 2: Diagnose missing updates
If the read model is not updating, likely the events are not being sent or processed properly.Final Answer:
The command handler is not sending events to update the read model -> Option BQuick Check:
Missing events cause read model stale data [OK]
- Blaming database corruption without evidence
- Confusing query side roles
- Assuming synchronous updates cause deadlocks here
Solution
Step 1: Understand scalability needs in CQRS
Separating reads and writes allows scaling read replicas independently to handle high traffic.Step 2: Use event sourcing for asynchronous updates
Event sourcing helps keep read models updated asynchronously, balancing freshness and availability.Step 3: Evaluate other options
Single database limits scalability; synchronous updates reduce availability; disabling caching hurts performance.Final Answer:
Use event sourcing to asynchronously update read models and deploy multiple read replicas -> Option AQuick Check:
Event sourcing + read replicas = scalable, fresh reads [OK]
- Using single DB limits scalability
- Synchronous updates reduce availability
- Disabling cache hurts performance
