FjellSystem Design Lab
Lesson 15.1• 9 min read

Requirements, estimates and the ranking API

The primer’s use cases and estimates (40 bytes per transaction, 40 GB a month, 400 writes and 40,000 reads per second) and its data and API.

What you will learn

  • State the sales rank feature's use cases and assumptions as the primer does.
  • Estimate its transaction and read rates and its storage.
  • See why the result itself is small, and where the hard part lies.

The problem

The primer's Design Amazon's sales rank by category feature covers only the ranking. The rest of the e-commerce site is out of scope.

Use cases (from the primer)

  • The service calculates the past week's most popular products by category.
  • A user views the past week's most popular products by category.
  • The service has high availability.

Assumptions (from the primer)

  • Traffic is not evenly distributed.
  • Items can be in several categories, cannot change categories, and there are no subcategories.
  • Results must be updated hourly; more popular products might need more frequent updates.
  • 10 million products in 1,000 categories.
  • 1 billion transactions and 100 billion read requests per month: a 100:1 read-to-write ratio.

Amazon describes its own Best Sellers Rank in its Kindle Direct Publishing help. Ranks are "relative", they "reflect recent and historical activity, with recent activity weighted more heavily", and they are "updated at least once per day". Amazon does not publish the formula.


Back-of-the-envelope estimates

Size per transaction (primer): created_at 5 bytes, product_id 8, category_id 4, seller_id 8, buyer_id 8, quantity 4, total_price 5, about 40 bytes.

40 B×109=40 GB of new transactions per month,1.44 TB in 3 years40\text{ B} \times 10^9 = 40\text{ GB of new transactions per month},\quad 1.44\text{ TB in 3 years}

With 400 requests per second ≈ 1 billion a month:

1×400=400 transactions per second,100×400=40,000 reads per second1 \times 400 = 400 \text{ transactions per second},\quad 100 \times 400 = 40{,}000 \text{ reads per second}

on average, and higher at peak. The exact 30-day figures are about 386 and 38,580 per second.

The result is small. Keeping the top 1,000 products for each of 1,000 categories is 10610^6 rows. Even at a few hundred bytes per row with indexes, that fits in memory. The hard parts are computing it from a billion transactions a month and serving 40,000 reads a second.


Data and API (from the primer)

Sales API servers write log lines, tab-delimited:

timestamp   product_id  category_id  qty  total_price  seller_id  buyer_id

The primer stores these raw logs in an object store such as Amazon S3. It writes the computed ranking to a sales_rank table with id, category_id, total_sold and product_id, indexed on id, category_id and product_id.

GET /api/v1/popular?category_id=1234

→ [ { "id": "100", "category_id": "1234", "total_sold": "100000", "product_id": "50" },
    { "id": "53",  "category_id": "1234", "total_sold": "90000",  "product_id": "200" }, ... ]

The Read API serves this from the sales_rank table, through a cache (lesson 4).

Figure 1: High-throughput real-time sales rank architecture with Anycast DNS, edge load balancing, decoupled async Kafka event streaming, Redis ZSET leaderboard cache, and persistent SQL replica storage.
  • Shoppers & Clients (42k RPS): 1 × Clients & Users
  • Route53 Anycast Geo-DNS: 2 × Anycast DNS / Route53
  • High-Throughput Ingress LB: 9 × Application Load Balancer
  • Sales & Read API Compute Tier: 26 × App Compute Server
  • Kafka Purchase Event Stream: 3 × Message Queue / Kafka
  • Redis ZSET Leaderboard Cluster: 7 × Redis In-Memory Cache
  • Primary Relational DB (ACID Writes): 2 × Primary Relational DB
  • Database Read Replicas (Cache Misses): 2 × Database Read Replica
  • Shoppers & Clients (42k RPS) sends requests to Route53 Anycast Geo-DNS
  • Route53 Anycast Geo-DNS sends requests to High-Throughput Ingress LB
  • High-Throughput Ingress LB sends requests to Sales & Read API Compute Tier
  • Sales & Read API Compute Tier sends requests to Kafka Purchase Event Stream
  • Sales & Read API Compute Tier sends requests to Redis ZSET Leaderboard Cluster
  • Sales & Read API Compute Tier sends requests to Primary Relational DB (ACID Writes)
  • Primary Relational DB (ACID Writes) sends requests to Database Read Replicas (Cache Misses)
  • Sales & Read API Compute Tier sends requests to Database Read Replicas (Cache Misses)
  • Kafka Purchase Event Stream sends requests to Sales & Read API Compute Tier

Key takeaways

  • 1 billion transactions and 100 billion reads a month are about 400 writes and 40,000 reads a second on average, a 100:1 ratio.
  • At about 40 bytes a transaction, the raw log grows by 40 GB a month.
  • The top 1,000 products for each of 1,000 categories is only 10610^6 rows, which fits in memory; the hard parts are computing it from the log and serving 40,000 reads a second.

Sources

Adapted from The System Design Primer by Donne Martin, licensed under CC BY 4.0. Changes were made.