3

I'm using pymongo in a web app, and want to do something of the form:

doc = collection.find(document)
doc.array1.append('foo')
for(y in doc.array2): <do things with y>
doc.array2 = filter(lambda x: ..., doc.array2)
doc.x = len(doc.array2)
collection.save(doc)

Is there any simple way way I can handle multiple requests dealing with the same document and prevent one from clobbering the results of another / be made invalid because it's editing a stale version?

James Harcourt
  • 153
  • 2
  • 6

2 Answers2

6

Take a look at the section in the mongodb docs on Atomic Operations

The section that might be of interest to you is the part about updating if it is still current.

  1. Fetch the object.
  2. Modify the object locally.
  3. Send an update request that says "update the object to this new value if it still matches its old value".

Should the operation fail, we might then want to try again from step 1.

This is the approach for when you have a number of operations to perform and you want to avoid holding a lock on the database. They also state in that doc how they are generally against holding a lock.

jdi
  • 90,542
  • 19
  • 167
  • 203
  • I saw how they're against holding locks, but I thought that was for collections rather than individual documents? I was hoping for a way to do this other than while : – James Harcourt Jul 17 '12 at 00:03
  • 1
    The doc explains the ways of doing that via the basic `update` query, or the more complex `find and modify` command. The issue for you is that based on your example, you want to do a bunch of local operations on the document data first to determine the new modified values. This would require the ability to hold the document in a locked state from the client side. They do not allow this functionality as they stated it could lead to potential deadlocks (if say your application terminated suddenly). – jdi Jul 17 '12 at 00:12
  • Does MongoDB (PyMongo in particular) have a builtin construct to do this @jdi or will I have to do this manually? Can't seem to find. – Ali Oct 06 '15 at 09:59
  • @MuhammadAli it has been a while and I don't know if pymongo has simplified the approach or not but you can read this pattern on how to do an update query that only updates if the fields haven't changed since you pulled the document : http://docs.mongodb.org/manual/tutorial/update-if-current/ – jdi Oct 06 '15 at 10:06
  • 1
    @jdi's answer was about "optimistic locking", which is general pattern. it's good to read - https://stackoverflow.com/a/129397/3618853 (@Yaroslav Stavnichiy mentioned) – sngjuk Apr 27 '23 at 13:06
0

You can implement document locks at application level.

Usage scenario:

with DocumentLock(doc_id):
  # DocumentLock makes sure no other thread interferes
  doc=db.doc_collection.find_one(doc_id)
  ... # analyse and update the document
  db.doc_collection.save(doc)

I've implemented DocumentLock class for multi-threaded application and published it here.

This pessimistic locking is much easier to use than optimistic locking advised in official docs. It is also more efficient under certain conditions (and might be very inefficient under others, so you have to think and evaluate before using it).

Community
  • 1
  • 1
Yaroslav Stavnichiy
  • 20,738
  • 6
  • 52
  • 55