9 Eylül 2026 Çarşamba

Inventory Reservation Checkout

Giriş
Açıklaması şöyle
Shopify replaced Redis with MySQL for one of their most critical checkout workloads. The trick was surprisingly simple.


Every time a buyer clicks "Complete purchase," Shopify has milliseconds to answer one question: is that last unit still available?


If Shopify says yes when the item is already gone, two people could buy the same item, leading to a cancelled order, an apology email, and a loss of trust.


If they say no when the item is actually available, they lose a genuine sale.


For years, Shopify used Redis to handle inventory reservations during checkout. When a customer started buying an item, Shopify would reserve that item in Redis so another customer couldn't buy the same unit at the same time.


But the actual inventory lived in MySQL, so reservations and inventory updates happened in two different systems with no shared transaction. A payment could succeed while the inventory update failed, causing an oversell, or the inventory could be deducted while the Redis reservation remained, causing an undersell.


The obvious fix was to move reservations into MySQL too, so everything could happen in one atomic transaction.


But MySQL had already failed at this once. Their first attempt used a single row with a quantity column, and at checkout scale, every buyer fought over that one row. Locking it, waiting, retrying. It buckled.


This is where the magic comes in.


Instead of using one row per item with its available quantity, they used one row for each individual unit. So an item with 10 units gets 10 rows. To reserve three units, they simply grab any three available rows.

And then the key: MySQL 8's 𝗦𝗞𝗜𝗣 𝗟𝗢𝗖𝗞𝗘𝗗.


Normally, if a query wants rows another transaction has locked, it waits, everyone queues behind the same rows, and throughput collapses. SKIP LOCKED flips that: if some rows are locked, MySQL simply skips them and hands back other free ones. No waiting. No queue on a hot row.


So when 500 buyers check out the same product at once, they're not fighting over one row, they each quietly grab a different free unit and move on. Contention, which killed the naive design, basically disappears.


To keep it fast at huge scale, they cap the pool at ~1,000 rows per item and refill it from the ledger as it drains. Same idea, bounded.


That's the whole trick: turn "everyone fights over one number" into "everyone grabs a different row." A single database feature made MySQL viable for a workload people assumed needed specialized infrastructure.


The boring database you already run is often more capable than you think. MySQL didn’t just handle Shopify’s scale, it also made their system much simpler.

Caddy Reverse Proxy

Change host file
C:\Windows\System32\drivers\etc\hosts
Add

#Caddy
127.0.0.1 foo.com.local

Start caddy
caddy fmt --overwrite --config .\Caddyfile
caddy run --config .\Caddyfile

Import Root CA
caddy trust

To check Root CA go to
C:\Users\acelya\AppData\Roaming\Caddy\pki\authorities\local
You will see root.crt and root.key files

My certificate is at
C:\Users\acelya\AppData\Roaming\Caddy\certificates\local\foo.com.local


Check Root CA
Run
certmgr.msc

Go to "Trusted Root Certification Authorities -> Certificates"
You should see Caddy Local Authority

Visit
https://foo.com.local

Import Root CA To Remote Clients
Copy root.crt file to other client computers and install it into the "Trusted Root Certification Authorities" store
Command to import on Windows (run as Administrator)
certutil -addstore -f "ROOT" root.crt

Or via GUI
- Double click root.crt -> Install certificate
- Select Local Machine -> Place all certificates in the following store
- Browse to Trusted Root Certification Authorities -> OK -> Finish

Why root.crt and not intermediate.crt ?
- root.crt - The root CA certificate. Clients trust this once, then autimatically trust every certificate Caddy issues
- intermediate.crt - signed by root.crt, but clients need the root in their trust store to validate the chain. Distributing the intermediate alone doesn't help unless the root in already trusted.

Örnek Caddyfile
# Caddy auto_https normally binds to port 80 for HTTP -> HTTPS redirects
# disable_redirects keeps TLS on 443 but prevents binding to port 80
# Needed on Windows where port 80 required admin privileges
{
  auto_https disable_redirects
}

# HTTPS-only listener on port 44
foo.com.local:443 {
  tls internal

  # Swagger UI-served by the Spring Boot app
  handle /swagger-ui/* {
    reverse_proxy localhost:8080
  }

 # OpenAPI docs served by the Spring Boot app
  handle /v3/api-docs/* {
    reverse_proxy localhost:8080
  }
 # REST endpoints
  handle /api/* {
    reverse_proxy localhost:8080
  }
 # Everything else -> frontend
  handle {
    reverse_proxy localhost:3000
  }
}