List the key functional requirements for the system (Ask the AI for hints if stuck)...
List the key non-functional requirements (performance, scalability, reliability, etc.)...
Estimate the scale of the system. Consider daily active users, read/write ratio, storage requirements, bandwidth, and any relevant QPS calculations...
Assuming we have ~10-20M
New listings around 1M/day. average 12 posts/sec, pick 50-100/sec
Active listings: ~15-30M
Read:write ratio: ~1000:1
Search+browse QPS: ~20-50K peak
Storage (images dominate)
New metadata per month: ~30M * 5KB = 150GB
Older metadata should be put to cold storage, such as S3.
Active listing metadata should be in MySQL / NoSQL for fetching.
We can serve images over CDN
Define the APIs expected from the system. This is your chance to analyze and define the read and write paths so that you can come up with the high-level design...
Post /ad/create
request {
name: string,
description: string,
imageUrls: [] //string list,
category: enum
}
response:
200 ok with adId
400 bad request
500 internal server error
Post /ad/edit
request {
adId: string
name: string,
description: string,
imageUrls: [] //object list, each object contains raw and thumbnail url
category: enum
}
response:
200 ok
400 bad request
500 internal server error
DELETE /ad/edit/{adId}
response:
200 ok
400 bad request
500 internal server error
ads/search/
{
category: enum,
keyword: string
}
response:
matched results // array of ad objects
[
{
adId: string
name: string
description: string
imageUrls: [] //array of strings,
category: enum
}
]
Describe the overall system architecture. Identify the main components needed to solve the problem end-to-end. Use the diagramming tool to create a block diagram.
Ad write process: The client uploads images to Object storage (S3 for example), and the send the data including image urls, ad names and description to API Gateway. The API Gateway should redirect these to Advertisement Service, which then writes to Advertisement Store. (DynamoDB, for example). The advertisement service then update the Cache if needed. A CDC Job should be triggered by updates in this store and publish a message into index update queue, so that the index update should be decoupled from the API synchronous flow. An index updating job should be listening to the SQS job queue and update the search index based on the message. Search being down would not block the posting as the two process are decoupled.
Ad read process: The client should send a request to API Gateway, which then route it to advertisement service. It will then fetch the data of the ad from cache first, if there is a miss, fetch it from advertisement store. Once this data is returned to client, the client fetches the images from CDN.
Search process: The client should sent a search request to API Gateway, which then get routes to search service, and search service will use search index to fetch the list of ads that match the search, and returns to the client.
Define the data model. Identify the main entities, their attributes, and relationships. Consider the choice of database type (SQL vs NoSQL) and justify your decision based on access patterns...
PostgreSQL for advertisement Store: Schema
Listing schema:
adId: String id for the Ad
name: String name of the Ad
userId: Who created the ad
description: String description of the ad
image Urls: Array of image urls object (contain raw and thumbnail url)
createdAt: Timestamp of when this advertisement store is created
price: long, price of the item
location: city of the item
status: enum (active, inactive)
categoryId: id of category
Indexes:
(category_id, created_at DESC), (location, category_id)
Category Schema:
categoryId: string id of the category
name: name of category
parentId: the parent of current category, should be another category in this table
Deep dive into 2-3 key components. Explain how they work, how they scale, discuss tradeoffs, capacity, and any relevant algorithms or data structures.
Search Index Update: The search index can be updated asynchrounously, so that if the search system's outage would not affect people posting ads or reading a specific ad. This is achieved by using a CDC component which is triggered by advertisement update in the PostgreSQL, and that should dispatch a job to update the index in our queue (SQS or Kafka). A handler then reads from the job queue to update the index. This would make sure 100% coverage for search index updates. The jobs should have some idempotency key so that we can avoid duplicate updates.
Images are stored in S3 and read from CDN. We can set an expiry in the object storage so that only active ad's images are kept.
Client's requests to create / edit an ad should also contain an idempotency key, which would avoid duplicate updates due to network instability or temporal issues.
A redis cache can be used to keep most recently read ads to assist in the read path, reduce latency and cost. Whenever the advertisement service does a write (edit / delet) to an existing ad, the service should sync the cache if needed.
While the PostgreSQL schema serves as the primary source of truth, the data would asynchronously sync to Elasticsearch to handle combined searches efficiently. Instead of pulling records into the application layer to manually filter them, the backend constructs a single Elasticsearch bool query. It uses a must clause to calculate text relevance against the listing's name and description, while strictly isolating exact matches—like categoryId, location, and price ranges—into filter clauses. This design is highly efficient because the filters bypass the heavy text-scoring engine, leverage automatic bitset caching for common locations or categories, and guarantee that the database only sends the exact paginated subset of results over the network, completely preventing memory bottlenecks on the application server.
To ensure the search list-view loads instantly without downloading massive original files, I would implement an asynchronous serverless image pipeline. When a user uploads a photo, it is stored in a 'Raw' S3 bucket, which immediately triggers an S3 Event Notification to invoke an AWS Lambda function. This Lambda worker dynamically resizes and compresses the original file into a lightweight thumbnail, saves it to a 'Processed' S3 bucket, and updates the PostgreSQL listing record with the new thumbnail URL. This guarantees our list-view only serves highly optimized images distributed through a CDN like CloudFront, drastically reducing network latency and bandwidth costs without blocking the user's initial upload request.
To prevent a cache stampede on a viral listing, a distributed lock should be added using existing Redis instance (via a SETNX command). When a cache miss occurs, the backend attempts to acquire this lock for that specific adId. The single request that successfully acquires the lock is permitted to query PostgreSQL, update the Redis cache, and release the lock. All other concurrent requests that fail to get the lock will briefly wait and poll the cache again, ultimately reading the freshly updated data without ever touching the database.