List non-functional requirements for the system...
Estimate the scale of the system you are going to design...
Assuming 550M monthly users (medium size ecommerce), 160M daily users
load 300 products per day per user
600k products per second
peak qps will be 1.2M products a second.
Define what APIs are expected from the system...
/recommendedProducts?page=1
we'll start simple, frontend will query for recommendedProducts, which will return pages of products.
User_id will be inferred from headers. the return object schema will be list of
{
product_id,
product_name,
thumbnail_s3_path,
price
}
GET /products/{product_id}
return
{
description
product_images_s3_paths: list,
etc.
}
POST /purchases
send {products: list of
{product_id, quantity}
Defining the system data model early on will clarify how data will flow among different components of the system. Also you could draw an ER diagram using the diagramming tool to enhance your design...
Products table
{ product_id: primary_key, product_description, price, etc. }
we can use a postgres for this because using products is good for relational data, and as we add products into our system, we want strong consistency for it, which ACID handles well.
Elasticsearch cluster
we will refresh this db periodically with embeddings of every product. the schema will be {product, product_embedding}
Users table
schema: {user_id: primary_key, email, password, etc.}
use a postgres here as well, strong consistency and handling relationships is why we want to use this here.
User_embeddings: this will be a NoSQL key value store for fast retrieval during query time. it will be
You should identify enough components that are needed to solve the actual problem from end to end. Also remember to draw a block diagram using the diagramming tool to augment your design. If you are unfamiliar with the tool, you can simply describe your design to the chat bot and ask it to generate a starter diagram for you to modify...
Query flow
Client will call load balancer which will be in front of the web service.
Web service fetch the user_embedding from our key value table, and do KNN search against the ES cluster for top k results. (idk how to handle pages here. i think maybe we want to get more pages than needed, and return 1 page at a time?)
Offline flow
Whenever users click products, search, or purchase, create a log that holds this information along with the user_id.
We will have offline workers that process these logs into a dataset with user_id, product_id, engagement_type (clicked, searched, or purchased)
We will train on this dataset and refresh the ES cluster with product embeddings generated for the entire products table. We will also generate user embeddings and place them in the embeddings table.
Explain how the request flows from end to end in your high level design. Also you could draw a sequence diagram using the diagramming tool to enhance your explanation...
refer to description in high level design, supplemented by the sequence diagram.
Dig deeper into 2-3 components and explain in detail how they work. For example, how well does each component scale? Any relevant algorithm or data structure you like to use for a component? Also you could draw a diagram using the diagramming tool to enhance your design...
Because of massive scale, we should put a cache in front of our user embeddings KV store. As we refresh the DB, we can also put every known user into the cache as well. If we don't find an embedding for user, we can return a generic default embedding from the DB.
We can also precompute products for active users. For users with most product clicks (get stats for this offline), precompute this ES search and place it in a cache. So the new flow would be, query the user_id, if there are products in the cache, return them, else do KNN search.
Explain any trade offs you have made and why you made certain tech choices...
My products, users, and orders tables all use relation DB. they don't work as well at massive scale but reads on these should be quite small. The largest volume of high traffic reads will actually go to ES cluster, and we have a cache in front for active users that will eleviate hitting the ES cluster. Elasticsearch itself also scales quite well out of the box. (this is bs idk that)
Try to discuss as many failure scenarios/bottlenecks as possible.
the ES cluster is definitely a bottleneck, doing a more expensive KNN distance calculation, that is why we will use a cache. If this service is down, we should have a fallback of default products?
What are some future improvements you would make? How would you mitigate the failure scenario(s) you described above?