Assume daily active user is 10m.
Read QPS: 10m / 100k = 100
Peak read QPS: 100 * 2 = 200
Assume 10% user bid 1 item per day
Write QPS: 10m * 10% / 100k = 10
DB usage capacity:
Each bid may take up to 1kb to save.
Daily DB usage: 10m * 10% * 1kb = 1GB
Yearly DB usage: 1GB * 400 = 400GB
We need 3 replica. So yearly DB usage: 400GB * 3 = 1.2 TB
REST api:
WebSocket message:
I choose SQL db.
The main schema of Item table:
id (primary key)
name
highestBidId (reference key to bid table)
The main schema of bid table:
id (primary key)
itemId (reference key to item table)
status (enum: bidWon, bidLoss, bidComplete, bidFail)
price
createdAt
updatedAt
The design separate read and write requests workflow
Deep dive into the SQL Database
The most important part for the Database is to make sure there is no double bidding for same item.
To make sure double bidding won't happen, there are two locking mechanisms to make sure two requests won't update the same database row at the same time.
Since the auction system requires low latency, I will choose to use optimistic locking.
Deep dive into the bid write service
When horizontal scaling, the bid write service could be sharded by itemIds so to make the same item go to the same server. The server could have a local cache of the top bid for certain items. This is to reduce the request to DB if the new bid is lower than the local cache in the bid write service. Even if we introduce a state in the service, when the service failed, it is easy to recreate the state by querying the SQL DB cache. The cache DB data may not update to date, but it is good enough to do the first level check.
First trade offs when writing a new bid to DB.
There are three options:
For this design, I will choose option 3 considering the low latency and high availability requirement.
Try to discuss as many failure scenarios/bottlenecks as possible.
What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?