Free preview

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

ComponentDetail
Segments producerCommunicates with third-party map services (e.g. Google Maps) to divide the world into smaller regions called segments. This reduces the search scope
Quadtree serversManage trees containing places within segments. They process search requests based on the radius and the user's location
AggregatorsCollect results from quadtree servers and return a consolidated list to the user
Read serversHandle read requests, directing search queries to quadtree servers. Separating read traffic improves efficiency
Write serverHandle write requests (adding places, reviews, ratings) and update storage
SQL databaseStores 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 storesStores the list of places mapped to segment IDs, and quadtree data mapped to unique IDs, for low-latency access
Load balancerDistributes 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

WorkflowWhat happens
Searching for a placeLoad 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 feedbackLoad balancers route requests to write servers. These servers update the relational database and store images in blob storage
Making segmentsThe 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:

PathFrequencyCovers
Direct quadtree insertImmediateA new place appearing in search
Segment producer rebuildMonthlyRe-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.

Enjoying the preview?

Create a free account to unlock the rest of this course, the in-browser judge, and live AI mock interviews.

Sign up free to continue