Components and Workflow
In one line: the component list contains one genuinely unusual entry — a producer that talks to a third-party map service — and one split that is unusually well justified.
The components
| Component | Detail |
|---|---|
| Segments producer | Communicates with third-party map services (e.g. Google Maps) to divide the world into smaller regions called segments. This reduces the search scope |
| Quadtree servers | Manage trees containing places within segments. They process search requests based on the radius and the user's location |
| Aggregators | Collect results from quadtree servers and return a consolidated list to the user |
| Read servers | Handle read requests, directing search queries to quadtree servers. Separating read traffic improves efficiency |
| Write server | Handle write requests (adding places, reviews, ratings) and update storage |
| SQL database | Stores relational data (Users, Places, Reviews, Photos). SQL facilitates queries such as retrieving a user's visit history or a place's reviews, and ensures consistency |
| Key-value stores | Stores the list of places mapped to segment IDs, and quadtree data mapped to unique IDs, for low-latency access |
| Load balancer | Distributes incoming requests uniformly across servers |
Read and write servers are split, and here the split is genuinely earned
Separating reads from writes is a reflex in system design, and it is often weakly justified. Here it is the best-justified split in the chapter, because of the ratio from Lesson 3:
Writes: 5 new places per day + 1 million reviews per day Reads: 60 million users searching
New places arrive at roughly five per day. Searches arrive continuously. Even taking the conservative reading of the traffic, that is a read-to-write ratio in the millions to one for places.
Three consequences follow, and each shows up somewhere in the design:
The two paths touch different stores. A read hits the quadtree and then the relational database. A write hits the relational database, blob storage, and the quadtree index. Different dependencies, different failure modes.
They have opposite cost profiles. Reads must be fast and are enormously frequent. Writes are rare and may be expensive — which is exactly what licenses the costly quadtree split in Lesson 13 and the daily rating rebuild.
They scale independently. You will run thousands of read servers and a handful of write servers, and no amount of read traffic requires more write capacity.
The general form is worth carrying: when the read-to-write ratio exceeds a few thousand to one, reads and writes stop being two operations on one system and become two systems sharing a data model. Optimizing them together means compromising both.
The segments producer depends on a third party, which is a real risk the design does not price
"Communicates with third-party map services (e.g. Google Maps) to divide the world into smaller regions called segments."
This is unusual. Every other design in this course builds its own foundational data. Here a core structural input — how the world is divided — comes from outside the system.
The Google Maps chapter made a similar scoping decision, assuming "road data comes from external sources." But there the external data was content. Here it is structure: the segmentation determines how the index is organized.
Three risks worth naming, because none is addressed:
A dependency on a competitor. Google Nearby is named in Lesson 1 as a comparable service. Building your spatial partitioning on a competitor's API is a strategic exposure, not just a technical one.
Changes to their segmentation invalidate your index. If the third party re-partitions, your key-value store's segmentID → places mapping becomes wrong, and every quadtree built on it must be rebuilt.
It is unnecessary. Dividing a coordinate space into cells requires no map data at all — it is arithmetic on latitude and longitude. What you might want from a map service is land versus water, so you do not allocate segments over ocean. That is a real optimization, and a much smaller dependency than the one described.
Say this in an interview. A dependency that could be arithmetic is a dependency worth removing.
The aggregator exists because the query spans partitions
"Collect results from quadtree servers and return a consolidated list to the user."
Why is more than one quadtree server involved at all? Because a search radius does not respect partition boundaries. A user near the edge of a cell has relevant places in the neighbouring cells, and those may live on other servers.
So the aggregator is a scatter-gather coordinator, the same pattern as distributed search's query fan-out:
read server -> identify the cells intersecting the radius
-> query each cell's quadtree server IN PARALLEL
-> aggregator merges, sorts by rating, truncates to 20
Two properties come with that pattern and both matter.
Latency is the slowest participant. A search touching six cells takes as long as the slowest of six servers, so tail latency dominates — the same argument the Distributed Search and Google Maps chapters made about fan-out.
The final ranking can only happen at the aggregator. No individual quadtree server can know whether its best result beats another server's, so each returns its local top results and the aggregator picks globally. Lesson 13 makes this explicit: the quadtree returns "the top 50 or 100 popular places" and "the aggregator service determines the actual top places."
That over-fetch — 50 to 100 per cell to produce a final 20 — is the standard price of distributed ranking, and it is worth naming as such rather than treating as arbitrary.
The workflows
| Workflow | What happens |
|---|---|
| Searching for a place | Load balancers route requests to read servers, which query quadtree servers for places within the radius. Aggregators refine the results before returning them to the user |
| Adding a place or feedback | Load balancers route requests to write servers. These servers update the relational database and store images in blob storage |
| Making segments | The segments producer splits the map into segments. Places within each segment are stored in a key-value store. This process repeats periodically (e.g. monthly) to incorporate new places |
A monthly segment rebuild means a new place is invisible for up to a month
The third workflow says segmentation "repeats periodically (e.g. monthly) to incorporate new places."
Read that against the second workflow, which says adding a place "updates the relational database and stores images in blob storage" — and says nothing about the index.
Put together, a newly added restaurant is in the database immediately and in the search index up to a month later. For a business owner who has just listed their restaurant, that is not a minor staleness — it is the product not working.
The design does address this, but in a different lesson. Lesson 13's "insert a new place" describes inserting directly into the quadtree, splitting the leaf if needed. So there are really two update paths:
| Path | Frequency | Covers |
|---|---|---|
| Direct quadtree insert | Immediate | A new place appearing in search |
| Segment producer rebuild | Monthly | Re-partitioning as density shifts |
That is a coherent design — an incremental fast path plus a periodic full rebuild — and it is the same shape as distributed search's incremental indexing beside batch reindexing.
But the workflow section describes only the slow path and the write workflow omits the index entirely, so the reader is left with an apparent month-long delay that the design does not actually have. When a system has both an incremental and a batch path, describing only one makes the design look broken.
Two key-value stores doing two different jobs
The description bundles them: "stores the list of places mapped to segment IDs, and quadtree data mapped to unique IDs." The finalized design diagram shows two key-value stores, and it is right to.
segmentID → list of places is the static-segment index from Lesson 8. It is what the segment producer writes monthly.
serverID → quadtree contents is the durable backing for the in-memory trees, described in Lesson 12: "the key-value store makes it easier to rebuild the quadtree if we lose it."
Different keys, different writers, different purposes — one is an index, the other is a backup of an index. Worth separating when you describe the design, because conflating them makes it sound like the quadtree and the static segments are the same structure, when in fact the chapter replaces the first with the second.
Key takeaway
Eight components, of which the read/write split is unusually well earned — five new places a day against 60 million searches means reads and writes are two systems sharing a data model, which is what licenses expensive writes later. The aggregator is a scatter-gather coordinator, needed because a radius crosses partition boundaries, and it is the only place a global ranking can happen — hence over-fetching 50 to 100 per cell to return 20. The segments producer's third-party dependency is structural, not just content, and mostly avoidable since dividing coordinates is arithmetic. And the workflow describes only the monthly rebuild, omitting the immediate insert path that actually keeps new places searchable.
Next: the naive approach, and exactly why it fails.