BugForge
All lessons
Databases · 05/09 6 min1 hands-on

Updating safely

$set and $inc, getting the updated document back, and the race that oversells your stock.

Update operators

Product.updateOne({ _id: id }, { $set: { price: 25 } });   // set fields
Product.updateOne({ _id: id }, { $inc: { stock: -1 } });   // add or subtract
Post.updateOne({ _id: id }, { $push: { tags: "new" } });   // append to an array

updateOne resolves to a summary such as { matchedCount: 1, modifiedCount: 1 }, not the document.

Getting the document back

await Product.findByIdAndUpdate(id, { $inc: { stock: 5 } });                  // returns the OLD document
await Product.findByIdAndUpdate(id, { $inc: { stock: 5 } }, { new: true });   // returns the NEW one

This one trips everyone at least once.

The overselling race

const p = await Product.findById(id);   // A reads stock = 1   … B reads stock = 1
if (p.stock < qty) return false;        // both pass the check
p.stock -= qty; await p.save();         // both save: you sold 2 lamps you didn't have

Between your read and your write, another request can sneak in. The fix: let the database do the check and the change in one step.

const r = await Product.updateOne(
  { _id: id, stock: { $gte: qty } },    // only matches if there's enough
  { $inc: { stock: -qty } },
);
return r.modifiedCount === 1;           // 0 means someone else got there first

The exercise fires two buyers at the last lamp at the same moment. Watch the naive version fail, then fix it.

Your turn · 1 exercise0/1 solved

Update without losing stock

restock should return the updated product. buy must never sell more than is in stock, even when two orders arrive at once.

Loading editor…
Next in Databases
Relations and populate