MongoDB does not provide means to lock a document (like
SELECT FOR UPDATE in RDBMS).
There is a recommended approach to Isolate Sequence of Operations, which is sometimes called optimistic locking, but there are scenarios when this approach seems to be overly complicated or even inefficient (that is why pessimistic locks are still in use).
I am trying to implement generic (can be used with various databases) document (or record) locking at application level. Assuming I have only one application working with collection (or table). And my application is multi-threaded.
with DocumentLock(doc_id): # make sure no other thread interferes doc=db.doc_collection.find_one(doc_id) ... # analyse and update the document db.doc_collection.save(doc)
Here is my DocumentLock class:
class DocumentLock(object): _lock=RLock() # protects access to _locks dictionary _locks=WeakValueDictionary() def __init__(self, doc_id): self.doc_id=doc_id def __enter__(self): with self._lock: self.lock=self._locks.get(self.doc_id) if self.lock is None: self._locks[self.doc_id]=self.lock=RLock() self.lock.acquire() def __exit__(self, exc_type, exc_value, traceback): self.lock.release() self.lock=None # make available for garbage collection
So basically I have a dictionary of
RLock per accessed document. I use weak references to get rid of unused locks.
DocumentLock can be subclassed for every collection (or table) that needs record locking.
This all seems to work (although not tested under concurrency) but I am publishing it here for review and advise. Do you see any weaknesses or mistakes in this code? Is there a better approach to do this kind of things in Python?