| Users / Orders | 100 Orders/day | 10,000 Orders/day | 1,000,000 Orders/day | 100,000,000 Orders/day |
|---|---|---|---|---|
| Order State Transitions | Simple DB updates, single instance | Increased DB writes, possible queueing | High DB load, need async processing | Massive scale, distributed state management |
| System Components | Single app server, monolithic state logic | Multiple app servers, load balancer | Microservices for order states, event-driven | Global distributed services, CQRS, event sourcing |
| Database | Single relational DB instance | Read replicas, connection pooling | Sharding, partitioning by order ID or region | Multi-region DB clusters, eventual consistency |
| Message Queues | Not required or simple queue | Basic queues for async state changes | Robust event queues, retry mechanisms | Distributed event streaming platforms (Kafka, Pulsar) |
| Latency | Low, synchronous updates | Moderate, some async processing | Higher, eventual consistency accepted | Latency optimized with caching and event sourcing |
Order state machine in LLD - Scalability & System Analysis
Start learning this pattern below
Jump into concepts and practice - no test required
The database becomes the first bottleneck as order volume grows. Each order state change requires a write operation. At around 10,000 orders per day, the DB write load increases significantly, causing slower response times and potential contention.
- Read Replicas: Offload read queries to replicas to reduce DB load.
- Connection Pooling: Efficiently manage DB connections to handle more concurrent requests.
- Asynchronous Processing: Use message queues to decouple state changes from user requests.
- Sharding: Partition the database by order ID or region to distribute load.
- Event Sourcing: Store state changes as events to improve scalability and auditability.
- Microservices: Separate order state logic into dedicated services for better scaling.
- CDN and Caching: Cache order status responses where possible to reduce DB hits.
Assuming 1,000,000 orders/day (~11.6 orders/sec):
- DB writes: ~12 QPS (writes per second) for state changes.
- DB reads: Assuming 10 reads per order, ~120 QPS reads.
- Storage: Each order state event ~1 KB, daily ~1 GB storage needed.
- Network bandwidth: Assuming 10 KB per order state API call, ~116 KB/s (~0.9 Mbps).
- Server capacity: One app server can handle ~1000 concurrent connections; multiple servers needed for load balancing.
Start by describing the order state machine and its transitions. Then discuss expected load and identify the first bottleneck (usually the database). Next, explain scaling strategies like asynchronous processing and sharding. Finally, mention trade-offs such as consistency vs latency and how event sourcing can help.
Your database handles 1000 QPS. Traffic grows 10x to 10,000 QPS. What do you do first?
Answer: Introduce read replicas and connection pooling to distribute load and reduce contention. Also, implement asynchronous processing with message queues to decouple writes from user requests, preventing DB overload.
Practice
What is the main purpose of an Order State Machine in a system?
Solution
Step 1: Understand the role of state machines
State machines define allowed states and transitions for an entity, ensuring valid progress.Step 2: Apply to order lifecycle
For orders, the state machine controls stages like 'Pending', 'Shipped', 'Delivered', preventing invalid jumps.Final Answer:
To track and control the valid states an order can be in during its lifecycle -> Option AQuick Check:
Order state machine = control order states [OK]
- Confusing state machine with payment processing
- Thinking it calculates prices
- Mixing with user session management
Which of the following is the correct way to represent a state transition in an order state machine?
class OrderStateMachine:
def __init__(self):
self.state = 'Pending'
def ship(self):
# Transition from Pending to Shipped
?
Solution
Step 1: Understand valid state change syntax
Assign new state only if current state allows it; else raise error.Step 2: Check each option
if self.state == 'Pending': self.state = 'Shipped' else: raise Exception('Invalid transition') correctly assigns 'Shipped' if current is 'Pending', else raises exception.Final Answer:
if self.state == 'Pending': self.state = 'Shipped' else: raise Exception('Invalid transition') -> Option AQuick Check:
Valid transition check = if self.state == 'Pending': self.state = 'Shipped' else: raise Exception('Invalid transition') [OK]
- Using comparison (==) instead of assignment (=)
- Assigning wrong state based on condition
- Changing method name instead of state
Given the following code snippet for an order state machine, what will be the output after calling cancel() twice?
class OrderStateMachine:
def __init__(self):
self.state = 'Pending'
def cancel(self):
if self.state in ['Pending', 'Shipped']:
self.state = 'Cancelled'
else:
print('Cannot cancel from', self.state)
order = OrderStateMachine()
order.cancel()
order.cancel()
print(order.state)
Solution
Step 1: Trace first cancel call
Initial state is 'Pending', so state changes to 'Cancelled'.Step 2: Trace second cancel call
State is now 'Cancelled', so print message 'Cannot cancel from Cancelled' and state stays 'Cancelled'.Final Answer:
Cannot cancel from Cancelled\nCancelled -> Option CQuick Check:
Second cancel prints message, state remains Cancelled [OK]
- Assuming second cancel changes state again
- Ignoring printed message
- Expecting error instead of print
Identify the bug in this order state machine method that allows invalid state transitions:
def deliver(self):
if self.state == 'Shipped' or 'Out for Delivery':
self.state = 'Delivered'
else:
raise Exception('Invalid transition');
Solution
Step 1: Analyze the condition logic
The condition uses 'if self.state == 'Shipped' or 'Out for Delivery'', which always evaluates True because non-empty strings are truthy.Step 2: Correct the condition
It should be 'if self.state == 'Shipped' or self.state == 'Out for Delivery'' to check both states properly.Final Answer:
The condition always evaluates to True due to incorrect or usage -> Option DQuick Check:
Incorrect or condition causes always True [OK]
- Using 'or' with string literals incorrectly
- Forgetting to compare both sides explicitly
- Assuming condition works as intended
You are designing an order state machine for an online store. The order states are Pending, Confirmed, Shipped, Delivered, and Cancelled. Which design ensures scalability and prevents invalid transitions?
Choose the best approach:
- Use a dictionary mapping each state to allowed next states.
- Hardcode all transitions in if-else blocks.
- Allow any state to transition to any other state.
- Use a single variable without validation.
Solution
Step 1: Evaluate scalability and validation needs
Hardcoding transitions is error-prone and hard to maintain; allowing any transition breaks rules.Step 2: Choose dictionary mapping
Mapping states to allowed next states centralizes rules, making it easy to update and validate transitions.Final Answer:
Use a dictionary mapping each state to allowed next states -> Option BQuick Check:
Dictionary mapping = scalable, validated transitions [OK]
- Hardcoding transitions everywhere
- Skipping validation of transitions
- Allowing invalid state jumps
