AlgoMaster Logo

Designing a Proximity Service like Yelp

medium26 min readUpdated June 23, 2026
Listen to this chapter
Unlock Audio

In this chapter, we will explore the high-level design of a platform like Yelp.

Let’s start by clarifying the requirements.

1. Requirements

Before diving into the architecture, lets clearly define what we’re building.

Functional Requirements
  1. **Search & Discovery: **Users can search for nearby places (restaurants, gyms, salons, etc.) using their current location. Support filters like:
    • Distance (e.g., within 2 km, 5 km)
    • Category (e.g., cafe, spa, grocery store)
    • Ratings (e.g., 4 stars and above)
    • Open Hours (e.g., currently open)
  2. **Business Listing Details: **Each place should have a detailed page showing:
    • Name, address, contact
    • Photos, opening hours, services
    • Location on a map
    • User reviews and ratings
  3. **Reviews & Ratings: **Users can leave ratings (1 to 5 stars) and written reviews.
  4. **Business Management: **Business owners can create a new listing or update details
  5. **Favorites: **Users can bookmark or save favorite places for quick access later.
Non-Functional Requirements
  1. **Scalability: **The system should handle millions of users and tens of millions of listings and reviews
  2. **Low Latency: **Search results should be returned within <200ms
  3. **High Availability: **The service should remain operational even if individual components fail. Availability target: 99.99% uptime
  4. **Eventual Consistency: **For certain operations (e.g., newly posted reviews or updated listings), the system can be eventually consistent rather than strongly consistent.

2. Capacity Estimation

Storage Estimation

Business Listings

  • Assume 100 million listings
  • Each listing ≈ 2 KB (name, category, address, etc.)
  • Total = ~200 GB

Relatively small footprint, fits well in relational or document DBs. Queryable fields (category, rating, location) should be indexed.

Reviews

  • Assume 1 billion reviews (historical)
  • Data per review ≈ 1 KB (text + metadata)
  • Total = 1B x 1KB = ~1 TB

Media (Photos)

  • Average photos/business: 5
  • Size per photo: ~200 KB (optimized JPEG or WebP)
  • Storage = 100M listings × 5 × 200 KB = ~100 TB

Stored in object storage like Amazon S3, not in databases. Backed by CDN for efficient global delivery.

Favorites

  • Assume 100M total saved favorites
  • Data per favorite ≈ 50 bytes (user_id, business_id, timestamp)
  • Total = 100M × 50bytes = 5GB

3. High-Level Architecture

The proximity service follows a microservices architecture, where each core domain (Search, Listings, Reviews, Users) is managed by an independent, stateless service.

Clients

Users and business owners interact with the system through APIs using web and mobile clients. Users can search for nearby places, submit reviews, and save favorites or bookmarks, while authenticated business owners can create and update listings.

API Gateway / Load Balancer

The API Gateway acts as the single entry point for all client requests. It distributes incoming traffic across multiple service instances and handles authentication, authorization, rate limiting, and request logging.

Search Service

The Search Service handles all location-based search queries, supporting filters such as distance, category, minimum rating, and open now. It queries a Geospatial Index to locate nearby businesses within a radius and uses in-memory caching (e.g., Redis) to serve popular queries with low latency.

Search / Geospatial index

This is a specialized index that supports efficient proximity-based lookups. It stores each business's latitude and longitude, maps coordinates to business IDs, and supports fast geo-radius queries.

Listing Service

Business owners use the Listing Service to create or update listings. It handles CRUD operations for business profiles such as name, address, category, hours, and location, and writes updates to the Listings Database. It also emits events via a message queue to trigger search index updates and to invalidate or refresh cached data.

Review Service

The Review Service manages all user-generated reviews and ratings. It stores reviews in the Reviews Database, calculates and updates average ratings for businesses, and publishes events (e.g., review.created) for downstream consumers.

User Service

The User Service manages user accounts, authentication, and user-specific data like favorites and bookmarks. It stores this data in the Users Database.

Media Storage (e.g., S3)

Media Storage holds photos, logos, and media assets uploaded by business owners or users. Assets are served via CDN for fast delivery, and the media URLs are stored in the Listings or Reviews databases.

Search Cache

The Search Cache is an in-memory cache (e.g., Redis) that serves hot search queries. It improves latency and reduces load on the underlying services and databases.

Message Queue (Kafka / RabbitMQ)

The message queue powers an event-driven architecture. It decouples write operations from downstream processing, enables asynchronous updates to the search index and cache, and lets services publish and subscribe to domain events such as business.updated and review.created.

Index Updater

The Index Updater is a background consumer that listens to events from the message queue. It updates the geospatial and search index with new or modified business data, ensures eventual consistency between the primary datastore and the search layer, and can scale independently based on indexing workload.

4. Database Design

To support millions of users, businesses, and reviews while ensuring low-latency queries and high availability, the system uses a polyglot persistence approach, choosing the right type of database for each microservice based on its data access patterns and scalability needs.

4.1 Listings Database

Stores structured metadata about each business.

Recommended Database: PostgreSQL with PostGIS works well when we want strong relational integrity plus geospatial indexing. MongoDB is a good alternative when we prefer flexible, document-based storage.

Schema: businesses

Indexing: We add a geospatial index on location, plus B-tree indexes on category (for filtered search) and average_rating (for sorting or threshold-based queries).

4.2 Reviews Database

Stores user-generated reviews and ratings.

Recommended DB: We can use either a SQL (e.g., PostgreSQL) or NoSQL database (e.g., MongoDB) since both can meet our requirements.

Schema: reviews

Indexing

  • Compound index on (business_id, timestamp) to retrieve recent reviews quickly
  • Index on user_id for showing user’s review history

Aggregation: A periodic job or event-driven update to the Listings DB recalculates average_rating.

4.3 Users Database

Stores user profiles, credentials, and favorite listings.

Recommended DB: PostgreSQL or MongoDB

Schema: users

Schema: user_favorites

4.4 Geospatial Index

Enables efficient proximity lookups based on (latitude, longitude).

  • PostGIS (PostgreSQL) for precise geospatial queries
  • Elasticsearch for full-text + geo-combo queries
  • RedisGeo for blazing fast in-memory geo-radius queries

Stored Fields

  • business_id
  • latlon
  • optional filters like categoryrating

We will discuss more on this later in the deep dive.

4.5 Media Storage

Store business imageslogos, and user-uploaded photos associated with reviews.

Recommended Storage: Amazon S3 or any blob store

Metadata Storage: The image URL is stored in the Listings or Reviews DB.

Media is served via CDNs (CloudFront, Cloudflare) to reduce latency and bandwidth usage.

4.6 Caching Strategy

What to Cache

  • Hot search queries and results (e.g., “cafes in NYC”)
  • Frequently viewed business profiles
  • Recent reviews for popular listings

Recommended StorageRedis Cluster

Keys

  • search:{lat}:{lng}:{category}:{open_now}
  • business:{business_id}
  • reviews:{business_id}

4.7 Message Queues

Enable asynchronous, decoupled communication between services.

Technology: Kafka or RabbitMQ

Common Events

  • business.updated: Triggers search index refresh + cache invalidation
  • review.created: Triggers rating recalculation + cache update

5. API Design

Each microservice exposes a set of RESTful HTTP APIs. Authentication is handled via OAuth2 or JWT at the API Gateway level.

Let’s walk through the key APIs for each service.

5.1 Search Service

Description: Search for nearby businesses based on location, filters and keyword.

Query Parameters:

  • lat (required): Latitude
  • lon (required): Longitude
  • radius: Search radius in kilometers (default: 5)
  • category: Business category (e.g., cafe, gym)
  • min_rating: Minimum average rating (e.g., 4.0)
  • open_now: Boolean (true/false)
  • q: Optional keyword (e.g., "vegan", "wifi")

Example Request:

Response:

5.2 Listing Service

GET /businesses/{id}

Description: Get details of a business by ID.

POST /businesses

Description: Create a new business listing (authenticated business owner).

Request Body:

PUT /businesses/{id}

Description: Update an existing business listing.

DELETE /businesses/{id}

Description: Delete a business listing (rare, admin-only or soft-delete).

5.3 Review Service

GET /businesses/{id}/reviews

Description: Get all reviews for a business (paginated).

Query Parameters:

  • pagelimit
  • sort_byrecentratinghelpfulness

POST /businesses/{id}/reviews

Description: Submit a review for a business.

Request Body:

PUT /reviews/{review_id}

Description: Update your review.

DELETE /reviews/{review_id}

Description: Delete your review.

5.4 Favorites (User Bookmarks)

POST /favorites/{business_id}

Description: Add a business to user’s favorites.

DELETE /favorites/{business_id}

Description: Remove a favorite.

GET /favorites

Description: Get a list of favorite businesses for the logged-in user.

6. Design Deep Dive

6.1 Location-Based Indexing and Querying

Efficient proximity search is the backbone of a Yelp-like service. When a user searches for places near their current location, the system must return relevant results in milliseconds even when dealing with tens of millions of businesses.

A naive approach, scanning every record and calculating the distance from (lat, lon), is too slow. Instead, we rely on geospatial indexing to reduce the search space and enable low-latency, scalable lookups.

Let’s explore the most effective strategies and their trade-offs.

Geohash Grid Indexing

Geohashing is a widely used and efficient technique for indexing spatial data. It converts a (latitude, longitude) coordinate into a base-32 encoded string, where geographically close points share common prefixes.

This makes geohash ideal for proximity queries, enabling fast prefix-based lookups in large datasets using standard indexing mechanisms (e.g., B-tree or sorted string index).

How Geohashing Works

  • The world is initially divided into a large grid (e.g., 32x32).
  • Each grid cell is recursively subdivided into smaller cells.
  • Each subdivision adds one more character to the geohash string. The longer the geohash string, the smaller and more precise the area it represents.

In our system, we typically store a 6- or 7-character geohash per business, which gives a good balance between search granularity and index size.

Database Integration

To enable fast geospatial queries using geohash:

  • Add a geohash column to the listings table.
  • Index this column to enable fast prefix-based range queries.
  • Store the geohash at one or more precision levels (e.g., geohash_5geohash_6).

Querying Within a Radius

To search for nearby businesses:

  • Compute the geohash of the center point (user’s location) at the required precision.
  • Query for businesses where geohash LIKE 'prefix%' (e.g., WHERE geohash_6 LIKE 'dpz5%').
  • Also query 8 neighboring cells to cover boundary areas.
  • Post-filter results by computing actual distance to eliminate false positives.

A geohash prefix query is effectively a range scan on a sorted string, which is significantly faster than computing lat-long distances row-by-row.

Handling Varying Data Density

Geohashing works well at global scale, but resolution should adapt based on area density:

  • Urban/Dense Areas: Use longer geohashes (6-7 chars) to narrow down results.
  • Rural/Sparse Areas: Shorter geohashes (4-5 chars) are sufficient.

You can dynamically adjust precision based on the user’s search radius. For example:

  • 1 km → use geohash length 6
  • 5 km → use geohash length 5

For faster lookups, you may store multiple geohash levels (e.g., geohash_5geohash_6) as separate columns, avoiding the need for LIKE queries.

Scaling the Geohash Index

Geohash indexing is efficient even at large scale:

  • Example: 50 million businesses × ~500 bytes = ~25 GB
  • Easily handled by a modern SQL engine with proper indexing

Sharding Strategy:

  • Range-based sharding (e.g., in CockroachDB, Vitess):
    • Shard by geohash prefix (e.g., first 2-3 characters)
    • Ensures prefix queries stay on a single or few shards
  • Hash-based sharding (e.g., in DynamoDB):
    • Not optimal, neighbor cell queries may hit multiple partitions

Caching Hot Geohash Queries

Many users search in the same locations (e.g., city centers). These hotspots can be cached for sub-millisecond lookups.

Example Redis Key Structure:

Frequently accessed geohash and filter combinations are stored in Redis or a memory cache. This reduces read load on the database and delivers instant search results for common queries.

Quad-tree Spatial Index

Quadtree is a hierarchical spatial data structure that recursively subdivides a 2D plane into four quadrants: Northwest (NW), Northeast (NE), Southwest (SW), and Southeast (SE). Each quadrant becomes a node, which can be further subdivided if it contains more than a threshold number of data points.

This approach naturally adapts to data density:

  • Dense areas get subdivided more deeply (higher resolution).
  • Sparse areas remain coarse with fewer subdivisions.

This makes quadtrees particularly well-suited for spatial data with uneven distribution, like a city map where downtown has thousands of businesses but the outskirts have very few.

How It Works

  1. The root node covers the entire spatial region (e.g., the whole map).
  2. If a node contains too many points (e.g., 100), it’s split into 4 child quadrants.
  3. This subdivision continues recursively until:
    • Each node contains a manageable number of points, or
    • A maximum depth is reached.

Each node either stores data points directly or pointers to its children. The result is a tree where each node has up to 4 children, and leaf nodes represent spatial buckets.

Querying with Quadtrees

To perform a proximity search (e.g., "cafes within 2 km"):

  1. Start at the root node.
  2. Traverse only those quadrants that intersect the search circle.
  3. Prune entire quadrants that lie completely outside the radius, with no need to scan their children.
  4. At leaf nodes, compute actual distances to filter valid results.

This approach is fast and efficient. It avoids scanning irrelevant regions entirely and provides a tighter spatial fit than fixed-grid systems like geohash, especially for irregularly shaped queries.

In practice, quadtrees are typically implemented in-memory on each search server.

Example

At startup, the Search Service loads all business coordinates and builds a quadtree. When a search request arrives, it performs a quadtree traversal to return nearby results.

Trade-offs and Challenges

Despite their theoretical advantages, quadtrees come with several operational complexities:

Building the Tree

  • Constructing a quadtree over millions of points can take several minutes.
  • It consumes significant memory.
  • One workaround is to rebuild the tree periodically (e.g., nightly) and reload it across all servers.

Real-Time Updates

  • Supporting real-time insertions or deletions is non-trivial:
    • Requires lockingthread-safe updates, and potential tree rebalancing.
    • Difficult to do safely in distributed environments.

Lack of Shared State

  • Since quadtrees are typically kept in-memory per server, they:
    • Are not centrally consistent.
    • Must be rebuilt or synchronized when servers restart or scale up.

When to Use Quadtrees

Quadtrees can be a great fit when data changes infrequently, fast in-memory search is required, you have tight control over memory and indexing infrastructure, and you’re building a custom geo service optimized for spatial patterns.

In real-world systems, simplicity, reliability, and ease of scaling often take priority over raw algorithmic elegance. That’s why many production systems avoid quadtrees in favor of geohash-based indexing or database-native spatial indexes.

Database Spatial Index (R-Tree, etc.)

Another common approach to proximity search is to use the native geospatial indexing capabilities built into modern databases.

Instead of managing geohash encodings or building custom data structures, we let the database engine handle spatial indexing internally often using advanced data structures like R-Trees.

How Built-in Spatial Indexing Works

Databases like PostgreSQLMySQL, and MongoDB offer geospatial data types (e.g., POINTGEOGRAPHY) and spatial indexes optimized for proximity, containment, and range queries.

These systems abstract away the complexity of location indexing, letting you query locations with simple, intuitive functions like:

The database uses a spatial index (typically an R-Tree or variation) to quickly return relevant results.

Platform-Specific Support

PostgreSQL + PostGIS

  • PostGIS extends PostgreSQL with powerful geospatial features.
  • Supports GEOGRAPHY and GEOMETRY types.
  • Uses R-Tree-like indexes via GiST (Generalized Search Tree).
  • Ideal for precise "point within radius" queries (e.g., ST_DWithinST_Intersects).
  • Often integrates internal space-filling curve optimizations (like geohash or Hilbert curves).

MySQL

  • Supports spatial indexing on POINT data types.
  • InnoDB and MyISAM engines allow SPATIAL INDEX creation.
  • Limited compared to PostGIS (e.g., fewer spatial functions, weaker optimization).

MongoDB

  • Provides 2dsphere indexes for querying GeoJSON objects (points, polygons).
  • Supports rich queries: $near$geoWithin$geoIntersects.

R-Tree vs Geohash Indexing

R-Tree indexes offer higher precision and flexible spatial queries, but come with slightly higher write overhead and storage complexity. Geohash, in contrast, is lighter and simpler but less precise around region boundaries.

Trade-offs and Considerations

Using built-in spatial indexing simplifies early development, but introduces a few constraints:

Pros

  • No need to manually encode coordinates.
  • Supported out-of-the-box by most spatial DBs.
  • Rich query language (especially in PostGIS).
  • Ideal for medium-scale systems or internal tooling.

Cons

  • Vendor lock-in: Your implementation becomes tied to a specific database.
  • Scalability limits: At very high QPS or massive data volumes, general-purpose databases may struggle.
  • Harder to distribute: Unlike geohash (which is easy to shard by prefix), spatial indexes are harder to partition across nodes.

Our Design Choice

In our proximity service, database spatial indexes are a solid baseline, especially for early versions of the product.

However, as traffic grows and latency expectations tighten, geohash indexing is preferred for the following reasons:

  • Easier to implement and maintain
  • Works seamlessly with relational and NoSQL databases
  • Scales horizontally using standard sharding and caching techniques
  • Supports eventual consistency for business updates
  • Allows integration with existing infrastructure like Redis, PostGIS, and search engines

We may explore quadtrees in the future if we need:

  • Ultra-low latency on highly concentrated data
  • Specialized traversal or spatial partitioning
  • Complete control over spatial query behavior at runtime

But for now, geohash provides the right balance of simplicity, performance, and scalability.

6.2 Search Workflow: Step-by-Step

Let’s say a user opens the app and searches: "Find cafes within 2 km of my current location, open now, and rated 4+ stars."

1. Client Sends Search Request

The mobile/web client makes an HTTP GET request to the API Gateway:

2. API Gateway Routes Request

  • The API Gateway authenticates the user using the JWT.
  • Applies rate limiting, logs the request, and routes it to the Search Service.

3. Search Service Checks the Cache

The Search Service constructs a cache key:

It checks Redis for a cached result.

  • If found (cache hit): returns result to the user in <50ms.
  • If not found (cache miss): proceeds to the next step.

4. Geospatial Query Execution

The Search Service:

  • Computes a geohash or bounding box for the given (lat, lon, radius).
  • Queries the Geospatial Index to retrieve all businesses within the radius.

5. Filter & Rank Results

The raw result set is filtered based on:

  • Category (e.g., "cafe")
  • Open now (based on current time and open_hours)
  • Minimum average rating (e.g., 4.0+)

If needed, the Search Service fetches additional metadata (e.g., business name, thumbnail) from the Listings DB.

6. Response Construction

The final set of businesses is:

  • Sorted (e.g., by distance, rating, or popularity)
  • Paginated (e.g., top 20 results)

lightweight JSON response is constructed with key business fields:

7. Cache the Result

The Search Service stores the response in Redis using the same cache key, with a TTL (e.g., 2-5 minutes) to handle repeated requests efficiently.

8. Return Response to Client

The JSON result is sent back via the API Gateway to the client.

Total response time:

  • ~30-50ms if cached
  • ~100-200ms if uncached (includes DB + filtering + network)

6.3 Business Creation/Update Workflow

Let’s walk through what happens when a business owner creates or updates a business listing (e.g., updates name, address, or hours).

1. Client Sends Request

The business owner (authenticated) sends a POST or PUT request to the API Gateway:

2. API Gateway Routes Request

  • Verifies the JWT token.
  • Confirms the user is authorized (e.g., a business owner).
  • Routes the request to the Listings Service.

3. Listings Service: Validate & Write

  • Validates input (e.g., location coordinates, open_hours structure).
  • Generates a unique business_id (if creating a new listing).
  • Writes or updates the listing in the Listings Database.
  • Computes or updates: geohash for indexing.

4. Emit Domain Event

  • After the DB transaction is successful, the Listings Service publishes an event to the Message Queue:

This decouples the update from downstream processing and improves write latency.

5. Acknowledge the Client

  • Returns a 201 Created or 200 OK response to the client.
  • This ensures fast response time, even if indexing or caching is still in progress.

6. Background Consumers React

Index Updater

  • Consumes the business.updated event
  • Updates:
    • Search Index (e.g., Elasticsearch)
    • Geospatial Index (e.g., RedisGeo or PostGIS)
  • May reindex fields like name, location, category, and rating for search.

Cache Invalidator

This consumer invalidates any cached search results or business detail views involving this listing.

7. Follow-ups

7.1 High Scalability

Our system is architected for horizontal scalability at every layer, enabling it to handle millions of users, businesses, and real-time queries efficiently.

Let’s break this down by component:

Application Layer

All core services (Search, Listings, Reviews, Users) are stateless and run behind a load balancer. This allows us to scale linearly by adding more instances.

As traffic grows, we can spin up more containers or VMs (e.g., in a Kubernetes cluster or ECS) to match demand.

Data Layer

The data layer is partitioned (sharded) to prevent any single database instance from becoming a bottleneck:

  • Listings are sharded by geohash prefix or region (e.g., city or country).
  • Users and reviews can be sharded by user ID hash.

If we’re storing 200 million business listings, we distribute them across shards using the first few characters of the geohash. Each shard manages a manageable subset (e.g., 1-5 million businesses), allowing for low-latency queries even under global scale.

Read Scalability

Reads dominate most user-facing operations (e.g., searching nearby places, viewing business details). To absorb heavy read traffic:

  • Caching and read replicas are applied aggressively for relational and NoSQL databases. Frequently queried search results (e.g., popular areas) are cached in Redis, and the search and listings services query read-only replicas to offload the primary DB.
  • CDNs (Content Delivery Networks) handle static content. Business photos, icons, and even some pre-rendered HTML or API responses can be served from edge locations.

7.2 Latency

Our architecture is designed to keep most user-facing requests under 200ms, even at scale.

Efficient Geospatial Indexing

Our primary latency optimization starts with cutting down the search space:

  • We use geohash-based indexing to turn complex spatial range queries into simple prefix-based lookups.
  • A geohash query avoids full table scans and retrieves only nearby candidates, often using sorted index scans.

Result: Most geospatial queries complete in under 10ms at the DB level, even for datasets with millions of entries.

In-Memory Caching

We aggressively cache frequently requested data using Redis and local memory caches.

Examples

  • Popular geohash+filter combinations (e.g., "cafes near Times Square").
  • Business metadata for frequently viewed listings.
  • Static ranking responses (e.g., "Top 10 restaurants in NYC").

Cache hit path:

  • Redis read: ~5-10ms
  • Network + minimal processing: ~10-30ms
  • Total response time: often <50ms

Even cache misses are optimized, thanks to indexed reads and minimal post-processing.

Pagination and Payload Control

To minimize payload size and speed up response times:

  • We paginate results, e.g., 20 results per page.
  • This limits the amount of data serialized and sent over the network, the memory footprint on the server, and the processing or rendering time on the client.

This also improves perceived performance. Users see results quickly, and can request more as needed.

Global Deployment and Proximity Routing

When deployed across multiple geographic regions, we route users to their nearest datacenter or edge location:

  • A user in Singapore hits servers in Asia.
  • A user in London is routed to EU-West servers.

This drastically reduces network round-trip time (RTT), often the biggest latency contributor in global apps.

For many users, local routing alone can shave off 100+ ms compared to centralized hosting.

7.3 High Availability

To ensure the system remains accessible even in the face of failures, we’ve adopted a multi-layered fault-tolerant architecture that avoids single points of failure, gracefully degrades when needed, and recovers automatically.

Redundant Service Instances

All microservices (Search, Listings, Reviews, etc.) are deployed as multiple stateless instances behind load balancers:

  • Load balancers perform health checks and route traffic only to healthy instances.
  • If one instance crashes, others immediately pick up the traffic, with zero downtime at the user level.
  • Auto-scaling groups (e.g., via Kubernetes or AWS ASG) ensure that failed nodes are replaced automatically.

Example: If the Search Service has 5 replicas and one goes down, traffic is rerouted without service interruption.

Resilient Data Stores

We use replicated, highly available storage systems to avoid data loss and ensure continuity:

Relational Databases

  • Primary-replica configuration with automatic failover.
  • Writes go to the primary, reads can be served from replicas.
  • In case of failure, a replica can be promoted within seconds.

NoSQL Databases

  • Multi-leader replication (e.g., in Cassandra or DynamoDB global tables).
  • Allows writes in multiple regions and avoids single-writer bottlenecks.

Caches

  • Redis is deployed in clustered mode or with replica + sentinel setup.
  • If a cache node fails, either another replica takes over or the system falls back to the database.

Graceful Degradation

Even when parts of the system fail, core functionality remains available. We design for partial availability, not all-or-nothing failure.

Examples

  • If the Search Index is unavailable, we show a fallback UI (“Try again later”) or return results from a simplified dataset.
  • If Redis cache is down, we fall back to querying the database directly, slower but still functional.
  • If the Review Service is down, the Business Service still returns listing info and omits the reviews temporarily.
  • If writes can’t be processed immediately, requests are queued for deferred processing.

7.4 Eventual Consistency for Non-Critical Paths

In large-scale distributed systems, immediate consistency is expensive and often unnecessary for user-facing features that are not mission-critical.

We deliberately adopt eventual consistency in parts of the system where slight delays in synchronization are tolerable, trading off strict real-time accuracy for higher availability, faster writes, and system scalability.

Search Index Lag

When a new business is added or an existing one is updated (e.g., name or category), those changes may not immediately reflect in search results.

  • The core data is written to the primary database instantly.
  • message/event is published to an update queue (e.g., Kafka).
  • The Search / GeoSpatial Index eventually picks up the change and reindexes the data.

Why this is okay

Users may not see their newly added business in search for a few seconds to a minute, which is acceptable, and the listing is still accessible via direct URL or the dashboard. This decoupling keeps search updates off the write path, which ensures low-latency write operations and higher availability.

Review Propagation

After a user submits a new review, the review is saved immediately to the Reviews DB. However, the aggregated average rating and review count shown on business detail pages may lag slightly.

Why this is okay

Users get immediate confirmation of their review submission, and the rating or count discrepancy resolves quickly through background aggregators or cache expiration. No global locking or synchronous updates are required, which keeps the system responsive and write-scalable.

This trade-off enables the system to handle high write throughput without bottlenecks.

Cache Coherency

We allow temporary staleness in data served from caches (e.g., Redis, CDN edge caches).

  • Business listings, search results, or review stats may be cached with TTLs (e.g., 1-5 minutes).
  • On an update, we invalidate the affected cache keys, but if the cache is missed or delayed:
    • The user may briefly see outdated information.
    • The source of truth (DB) is always correct and will sync soon.

Why this is okay

Users rarely notice a few seconds or minutes of delay in non-critical fields like business name or average rating. This allows for read-scaling and low-latency data access without constant DB hits.

Quiz

Design Yelp Quiz

20 quizzes