Engineering Note / 2026
Selling the last one twice
When two people buy the last item at the same moment, or a marketplace sends the same order twice, a store can sell stock it does not have. Why it happens, the fixes that look right but are not, and what we would ship instead.
Engineering note. A technical write-up, not client work. There is no client and no results are claimed; it shows how we would solve the problem.
- Type
- Engineering note, no client
- Industry
- E-commerce engineering
- Services
- Custom web applications, Cloud, DevOps & care
Why it matters
Overselling looks like a small bug and costs like a big one: a cancelled order, a refund, an apology and, on marketplaces, a seller rating that drops. It rarely shows up in testing, because testing is one person clicking at a time. It shows up on launch day, during a sale, or when a marketplace retries a webhook. The first half of this note explains the decision in plain words. The code and the options we weighed follow, for the engineers.
What we set out to answer
- Why a checkout that works for one shopper can fail for two at once
- Which common fixes really stop it, and which only make it rarer
- What a repeated order event from a marketplace does to the count
- What to do when marketplaces see the stock count late
The problem, step by step
How a store sells the last unit twice, the fix we would ship, and how a repeated marketplace event is made harmless.
Read the diagram as text
- Checkout A → Database: How many are left?
- Database → Checkout A: 1
- Checkout B → Database: How many are left?
- Database → Checkout B: 1
- Checkout A → Database: Save 0
- Checkout B → Database: Save 0
- Two orders for one unit. The count says 0, so nothing looks wrong.
Both checkouts read the same number before either writes. Each one is correct on its own; together they sell one unit twice, and the stock column still looks fine.
Read the diagram as text
- Checkout A → Database: Take one, if one is left
- Checkout B → Database: Take one, if one is left
- Applied one at a time
- Database → Checkout A: Done: 1 row changed
- Database → Checkout B: Sold out: 0 rows changed
- B hears “sold out” before paying, not after.
The check and the change become one statement. The database applies them one at a time, so only one checkout can take the last unit, and the other learns before payment, not after.
Read the diagram as text
- Marketplace → Order hub: Order 17
- Order hub → Database: Record order 17, take one
- Marketplace → Order hub: Order 17 again (retry)
- Order hub → Database: Record order 17
- Database → Order hub: Already recorded: no change
- Order hub → Marketplace: OK, to both deliveries
- One order, one unit, however often it arrives.
Marketplaces deliver order events at least once, so a retry can arrive while the first delivery is still being handled. Recording the event id under a unique key, in the same transaction as the stock change, turns the repeat into a harmless no-op.
The code
SELECT qty FROM stock WHERE sku = ?;
-- payment, network, other work
UPDATE stock SET qty = :qty_read_minus_one WHERE sku = ?;UPDATE stock SET qty = qty - 1
WHERE sku = ? AND qty >= 1;
-- 1 row changed: sold. 0 rows: sold out.BEGIN;
INSERT INTO orders (event_id) VALUES (?)
ON CONFLICT (event_id) DO NOTHING;
-- only if that inserted a row:
UPDATE stock SET qty = qty - 1 WHERE sku = ? AND qty >= 1;
COMMIT;Options we weighed
Choosing what not to build is most of the work.
What we would ship
- Take stock with a single conditional update, and treat “0 rows changed” as sold out before any payment is taken
- Give every incoming order event a unique key and record it in the same transaction as the stock change
- Push stock changes to marketplaces the moment they happen, rather than on a timer
- Hold back a unit or two only on items where a marketplace is known to update slowly
- Test checkout with many simultaneous buyers, because one person clicking will never find this
