Question 43 / 50
Must KnowMediumDebuggingConcurrency
Two checkouts can both buy the last Mechanical Keyboard. What is wrong, and how do you fix it?
const product = await products.findOne({ _id: productId });
if (product.stock < 1) throw new Error("out of stock");
await products.updateOne(
{ _id: productId },
{ $set: { stock: product.stock - 1 } }
);Think about it first.
Short answer
That is a read-then-write race. Both requests read stock 1 and both write 0. Decrement atomically with $inc and a filter that stock is still greater than zero, then check matchedCount.
Why?
Single-document $inc is atomic. The filter { stock: { $gt: 0 } } is the compare-and-swap. If you also need to write an order document, that is a second document — then you consider a transaction or a reservation pattern. The stock field alone does not need a transaction.
Example
const result = await products.updateOne(
{ _id: productId, stock: { $gt: 0 } },
{ $inc: { stock: -1 } }
);
if (result.matchedCount === 0) throw new Error("out of stock");Interview tip
Name the race, then $inc. Going straight to transactions here is a weaker answer.
Common mistake
Adding a transaction around the findOne and updateOne without changing the logic — the race can still happen depending on isolation and retries.
How did you do?
Cheat Sheet