Testing and decrementing in an atomic operation?

I have just traced down an annoying bug which is basicly a race condition. For the sake of the argument lets assume a very simple document structure like { _id : 'XXX', amount : 100 }.

Hundreds of these these documents exist inside a collection and are accessed by multiple writers which effectively try to lower the amount by any value but never below 0. Currently this is done with two queries:

  • The first query determines the current amount.
  • The second query is only fired if the amount is greater then the value to subtract and decrements the current amount.

This is of course prone to result in a mess when multiple threads do this simultaneously. But is there a way to do this in an atomic operation? I couldn't find anything like a test-and-set operation in the docs.

If its relevant: The code running these operations is serverside JavaScript running with multiple Node.js instances and the mongodb npm package.

Qualify the update to only update the doc if amount is greater than 0:

db.coll.update({_id: 'XXX', amount: {$gt: 0}}, {$inc: {amount: -1}})