One row for every sellable unit, and each checkout takes any free one

2 months ago

A thousand buyers grab the last one in stock in the same second. The store has to sell that unit once and only once. Shopify rebuilt that guarantee on the ordinary database that already held its records.

Ask

Ask about this presentation

Answers are generated from this presentation.

Chapters

  1. 0:00One row for every sellable unit, and each checkout takes any free one
  2. 0:13Finishing a sale needed two writes that could not be made one step
  3. 0:30Ten units in stock is now ten rows
  4. 0:40Thousands of checkouts reserve at the same time, because each takes a different free row
  5. 1:10The ceiling was connections, the limited number of callers a database serves at once
  6. 1:37What was impossible five years ago is possible now, with one database feature
  7. 1:45The fix was a shape change in a database Shopify already ran
Show transcript

One row for every sellable unit, and each checkout takes any free one

One row for every sellable unit, and each checkout takes any free one.

How Shopify sells the last unit exactly once, at Black Friday peak volume.
engineering deep-dive shopify.engineering

A thousand buyers grab the last one in stock in the same second. The store has to sell that unit once and only once. Shopify rebuilt that guarantee on the ordinary database that already held its records.

Finishing a sale needed two writes that could not be made one step

Finishing a sale needed two writes that could not be made one step.

Redis held the running count, and the store’s main database held the records.
$5.1M per minute
peak, Black Friday 2025 · Shopify’s figure
Redis
a separate high-speed system
one running count per item
→
→
two writes, never one step
sold, but never deducted
deducted, but still held
the store’s main database
the records, the source of truth
shopify.engineering

Shopify says its stores hit five point one million dollars a minute at the Black Friday peak. The old design kept the count of what was left in Redis, a separate high-speed system, apart from the store’s records. When those two drifted apart, a buyer could pay for stock already gone.

Ten units in stock is now ten rows

Ten units in stock is now ten rows.

Reserving three units moves three of those rows, in a single step.
the old shape
quantity: 10
→
the new shape
moved
unit
moved
unit
unit
unit
moved
unit
unit
unit
reserve 3 → move 3 rows
shopify.engineering

So Shopify moved the hold on your stock into that ordinary database. Instead of one count per item, every sellable unit gets its own row, its own line in the list.

Thousands of checkouts reserve at the same time, because each takes a different free row

Thousands of checkouts reserve at the same time, because each takes a different free row.

Reservations and records now live in one database, and one transaction covers both.
one row per sellable unit
✓ free taken
✗ held stepped over
✓ free
✓ free
✓ free taken
✗ held stepped over
✓ free
✓ free taken
✗ held
three checkouts, three different free rows · pool capped at 1,000 rows per item and location, refilled from the store’s records
FOR UPDATE SKIP LOCKED
the clause that skips a held row · MySQL 8, the store’s main database
take the stock · finish the sale → one all-or-nothing step

Each checkout uses a database feature called SKIP LOCKED: if the row you reach for is already taken, it hands you the next free one instead of making you wait. The crowd spreads across every free row instead of queueing behind one. Shopify caps that pool of rows so the search stays fast. And because that hold now sits in the same database as the records, taking the stock and finishing the sale are one all-or-nothing step. Shopify says that step removed the two-system mismatch entirely.

The ceiling was connections, the limited number of callers a database serves at once

The ceiling was connections, the limited number of callers a database serves at once.

Tagging every query showed which job held a connection, and for how long.
connection hold time, measured per caller
the hold on stock
one heavy caller among several
other checkout-path code
✗ holding connections longer than necessary
a fixed number of connections
/* conn_tag:checkout_completion */
Shopify built this tracking at the layer in front of the database
checkout-path cleanup
reads down 50% · transactions down 33%
Shopify’s figures
at flash-sale peak
writer CPU under 50% · reader CPU under 16%
after the cleanup and a raised concurrency setting · Shopify’s figures
shopify.engineering

Shopify still hit a ceiling, with the database well under its limits. A database takes only so many callers at once, like a desk with a fixed number of phone lines. So the team measured who held those lines longest. That hold was one heavy caller among several. Other checkout code was holding lines too long, because it had never hit the limit first. Clearing that up, and raising one long-untouched setting, lifted the ceiling.

What was impossible five years ago is possible now, with one database feature

What was impossible five years ago is possible now, with one database feature.

Shopify’s engineers say the same goes for settings left at old defaults.
“The answer is often in the plumbing, not the engine.”
— Shopify’s engineers
shopify.engineering · Emilie Noel · May 2026

Shopify’s engineers draw a wider lesson: before you buy a specialized system, your existing database may already be enough.

The fix was a shape change in a database Shopify already ran

The fix was a shape change in a database Shopify already ran.
shopify.engineering

One row for every unit, and an ordinary database sold the last one in stock exactly once.