Atomicity & Transactions
Single-document Atomic Operations
Update operators that increment, push and set in one round trip, without a transaction.
Atomicity is wasted if you read, change in Node, write. Two checkouts both read stock: 1, both write 0. $inc (and $push, $set, $bit) run on the server in one document lock. The CRUD module listed the operators; this lesson is why they are the concurrency primitive.
db.products.updateOne(
{ name: "Mechanical Keyboard", stock: { $gte: 1 } },
{ $inc: { stock: -1 }, $set: { updatedAt: new Date() } }
)
// matchedCount: 0 → sold out. No separate read.db.products.findOneAndUpdate(
{ name: "Mechanical Keyboard", stock: { $gte: 1 } },
{ $inc: { stock: -1 } },
{ returnDocument: "after" }
)Several operators in one updateOne are still one atomic write: $inc stock and $set a timestamp. findOneAndUpdate / findOneAndDelete are the same guarantee plus a returned document. Conditional stock is a filter, not a transaction. Two collections (order + stock) still need the next lesson or a product decision to accept a mismatch. Races that are not solved by $inc — last-write-wins on city — are the Concurrency module.
Interview question
How do you decrement stock without a race or a negative count?
updateOne with a filter that stock is at least the quantity, and $inc by minus that quantity — one atomic write. matchedCount 0 means I did not take stock. findOne, subtract in the app, $set stock is a race. A transaction is not required for this single document.