Atomic Read-Write-Modify: MongoDB’s findAndModify

We’ve already introduced the atomic update modifiers supported by MongoDB. These are very powerful and enable race-free write operations of many kinds—including array manipulation and increment/decrement. However, it is often necessary to be able to modify the document atomically, and also return the result of the atomic operation—in a single step.

For example, imagine a billing system. Each user document has an “account_balance” property. There may be writes which alter the account balance—let’s say an account top-up event which adds money to the account, and a purchase action which takes money from the account. Consider the following implementation:

# User X adds $20 to his/her account, so we atomically increment
# account_balance
dbh.users.update({"username":username}, {"$inc":{"account_balance":20}}, safe=True)
# Fetch the updated account balance to display to user
new_account_balance = dbh.users.find_one({"username":username},
    {"account_balance":1})["account_balance"]

This will work fine assuming no other writes to the account balance occur between the write and read operations. There is an obvious race condition. If a purchase action were to take place between the balance update and the balance read, the user may be displeased to be presented with a post-payment balance of less than what they expected!

# User X adds $20 to his/her account, so we atomically increment # account_balance dbh.users.update({"username":username}, {"$inc":{"account_balance":20}}, ...

Get MongoDB and Python now with the O’Reilly learning platform.

O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.