Overview

Pessimistic Locking with MongoDB

1 Comment

In this article, I’m going to sketch a pattern for implementing pessimistic locking with MongoDB. MongoDB is a document-orientated NoSQL datastore that does not support locking itself.

In some business processes it may be required that you have an exclusive access to a single document. That’s when you need something like pessimistic locking.

Requirements

The implementation should offer the following features:

  • Locking does not change the locked document itself
  • Locks can be removed manually or by a timeout

For the rest of this article, let’s assume we want to lock documents in a collection called workitem.

Schema Design

The basic idea is to maintain a separate collection holding the locks, e.g. workitem.lock. The schema of a lock may look like this:

{
        "_id" : ObjectId("5087b72181a445980ae47d13"),
        "ts" : ISODate("2012-10-24T09:38:41.173Z"),
        "aid" : "my_app_my_session_id_4711"
}
  1. _id: The _id of the locked document
  2. ts: Timestamp of the lock
  3. aid: Some id that unambiguously identifies the context of the application (it may be a session id, process id etc.)

Let’s set up the locking collection:

db.workitem.lock.drop();
db.createCollection("workitem.lock");
db.workitem.lock.ensureIndex( { ts: 1 }, { expireAfterSeconds: 30 } );
db.workitem.lock.ensureIndex( { aid: 1 }, {unique: false} );

I’ll give a detailed explanation of the indexes later on.

So we end up with the following collections:

> show collections
system.indexes
workitem         // collection for domain objects
workitem.lock    // collection for locks on workitem documents

Obtaining a Lock

Obtaining a lock consists of the following steps:

  1. Query your document doc (depending on the size of the document and complexity of the query it may be better to read the _id only or the whole document)
  2. Create the application id app_id. The application is free to choose the scope of this id. It may be the session id of a web application.
  3. Insert a lock {_id: doc._id, ts: <current timestamp>, aid: app_id}. The insert is an atomic operation. If it runs, your application successfully set the lock. If not, someone else already has the lock to your document.

Releasing a Lock Manually

Perform these steps to manually release a lock:

  1. Modify your locked document
  2. Delete the lock either by
    • _id: when the scope of the lock was a single document.
    • aid: when your application locked more than one document. Another use case may be when you used a session id of a web application. Assume the user closed the browser but the lock is still active. If you have some kind of session listener in your application server and a session timeout occurs, you can still release all locks belonging to that session.

    If the delete fails, someone else has already removed the lock. You can react to that be re-reading the document and comparing the versions etc.

Let MongoDB release the Lock

Since version 2.2 there is a new kind of index, a TTL index. It’s basically an index on a date key where you can specify a time to live (TTL) of documents inside a collection. That is exactly what we did when defining the index on the ts key:

db.workitem.lock.ensureIndex( { ts: 1 }, { expireAfterSeconds: 30 } );

MongoDB periodically checks the age of the documents and purges them from the collection (using a hearbeat of once per second).

If your application fails to remove locks, this TTL index kicks in as a fallback. The TTL value should by chosen appropriate. TTL indexes do not work on capped collections.

Summary

In case you need pessismistic locking inside your MongoDB application this article gives you some hints on how to implement it.

Of course, all applications accessing the collection with documents to lock will need to use the same strategy.

I’m looking forward to your pessimistic locking implementations. Let me know when you have build something cool.

Kommentare

  • Peter Schuler

    30. October 2012 von Peter Schuler

    Interessting post. Looks almost like we need a old-school database again ­čÖé

    I have two remarks:

    1. As I understand locking the scheme you describe looks more like Optimistic locking rather then pessimistic locking. Pessimistic locking means lock-in-advance-and-block-other while optimistic locking means look-for-conflicts. Off course by checking if the document is locked before accessing it you make the scheme a bit pessimistic.

    2. I would recommend against exposing you web server session ID to other systems. This is a violation of the direct object reference rule in the OWASP top 10. If some one would be able to access you DB you would expose all you users to a Session Hijacking attack as it would contain active session ID’s. It would be better to generate some unique random ID for he AID and store that value in you Http session and only communicate the session IDs to your clients browser by HTTPS.

    Just my two cents.

Comment

Your email address will not be published. Required fields are marked *