Assume that Alice and Joe both read the same data item from Couchbase Server, then they both changed the data, and then both tried to write the new versions back to the database. Whose changes should be saved? Alice’s? Joe’s? Neither? A combination? It’s questions like these that determine the winner in the debate over pessimistic vs optimistic locking.

Specifically, developers use locking to serialize access to shared data items. But which locking scheme should you pick for your application?

In this blog, I am going to explain optimistic locking vs. pessimistic locking and the differences between the two. We will also discuss the optimistic and pessimistic locking APIs that you can use in Couchbase Server to control concurrent access to your data.

What is Optimistic Locking in Couchbase Server?

Let’s assume we are building an online wikipedia – like application using Couchbase Server: users can update an article and add newer articles. Let’s assume Alice is using this application to edit an article on ‘bicycles’ to correct some information. Alice opens up the article and makes those changes but before she hits save, she gets distracted and walks away from her desk. In the meantime, let’s assume Joe notices the same error in the bicycle article and wants to correct the mistake.

If optimistic locking is used in the application, Joe can edit the article and save his changes. When Alice returns and wants to save her changes, either Alice or the application will want to handle the latest updates before allowing Alice’s action to change the document. Optimistic locking takes the “optimistic” view that data conflicts due to concurrent edits occur rarely, so it’s more important to allow concurrent edits.

What is Pessimistic Locking in Couchbase Server?

Now let’s assume that your business process requires exclusive access to one or more documents or a graph of documents. Referring to our previous example, when Alice is editing the document she does not want any other user to edit the same document. If Joe tries to open the page, he will have to wait until Alice has released the lock.

With pessimistic locking, the application will need to explicitly get a lock on the document to guarantee exclusive user access. When the user is done accessing the document, the locks can be removed either manually or using a timeout.


So which one should you pick?

The answer is that, there is no correct answer – it depends. You should pick your locking scheme based on your application requirements.

Unless you expect a document to be heavily contended, optimistic locking is going to be much lower overhead than pessimistic locking – grab the item you need, update it quickly and attempt to apply it. If some other actor in the system beat you to it, you can just retry till you succeed.

With pessimistic locking, you can get exclusive access to a given item – no other thread can access the item while it is locked. You need to be sure you release the lock during failures. Imagine, I have the cereal object and won’t give it up until I get the milk object. But you have the milk object and won’t give it up until you get the cereal object. Timeouts can be used to possibly break deadlocks or to deal with clients who fail to release the lock. 

For simplicity sake, users might pick the same locking strategy across their application. This works well if the access requirements of all the different objects across your application are the same but in reality, this is not the case – different application objects have different access requirements. For example, in the case of social games –

1. Character and player inventory information is heavily accessed during gameplay,

requiring fast read-write access. First choice – Use optimistic locking. Next choice –

Pessimistic locking

2. Player accounts and preferences are read during player login or at the start of a game

but not frequently updated. Optimistic locking would work well here too.

Obtaining a pessimistic lock in Couchbase Server

Obtaining a lock in Couchbase Server consists of the following steps :

  1. Use the get-and-lock API to retrieve a value for a given key and lock that key
  2. The application now has exclusive control over the document

Obtaining a optimistic lock in Couchbase Server

Obtaining a lock in Couchbase Server consists of the following steps :

  1. Use the check-and-set (CAS) API to retrieve a CAS revision number
Releasing a lock in Couchbase Server

Perform these steps to manually release a lock:

  1. Use CAS to modify the value and release the lock

Behind the scenes of CAS

CAS operation is a simple concept which can be used to build more advanced concurrency control mechanisms. CAS determines if an object has been updated by another client between the time it was initially read and the time the save was attempted. If the object is modified by another client, an error is raised and the application has to re-read the value and retry the operation. If objects are not in high contention, the transformation of the data is idempotent and retrying the operation is easily accomplished without too much wasted work, optimistic locking is a good solution that provides high performance in the average case.

A common way of interacting with CAS involves a CAS loop as shown in the community developed go memcached client.

For example: Imagine there are 3 clients as shown in the interaction diagram below. All of the three clients attempt to update object X using CAS – each client succeeds, one at a time.

You didn’t release the lock, let Couchbase do it for you

When you perform a get-and-lock operation you provide an expiration for the lock as a parameter. The default amount of time a key can be locked is 15 seconds. After 15 seconds, if you don’t release the lock manually, Couchbase releases it automatically.

Final Thoughts on Pessimistic and Optimistic Locking in Databases

Optimistic locking might not be the best solution for every situation. While some use-cases work well with optimistic locking, others might need stricter schemes like pessimistic locking.

Locking might not be good for all cases – your application can have a problem if there is a lock contention. A thread can hold a lock and be de-scheduled by the OS. Then all the threads that want to acquire this lock will be blocked. One option is to avoid locking altogether where possible by using atomic operations. These API’s can be very helpful on heavily contested data.

However, if you really need locking and feel that optimistic locking covers most of your applications’ use-cases, optimistic locking using CAS in Couchbase Server is so easy to implement there’s no reason you shouldn’t try.

Author

Posted by Don Pinto, Principal Product Manager, Couchbase

Don Pinto is a Principal Product Manager at Couchbase and is currently focused on advancing the capabilities of Couchbase Server. He is extremely passionate about data technology, and in the past has authored several articles on Couchbase Server including technical blogs and white papers. Prior to joining Couchbase, Don spent several years at IBM where he maintained the role of software developer in the DB2 information management group and most recently as a program manager on the SQL Server team at Microsoft. Don holds a master's degree in computer science and a bachelor's in computer engineering from the University of Toronto, Canada.

6 Comments

  1. Lock picking is an art of
    legally, and with the permission of the user, unlocking a lock by analyzing its
    components. Though lock picking is mostly associated with crime and theft, it
    is an essential skill for a locksmith.

  2. James D Bloom July 16, 2014 at 5:31 pm

    Is there a code example of this? When I tried locking an object using a pessimistic in multiple threads nothing happened, i.e. all threads could retrieve and save the object independently, as follows:

    fastChangingCouchbaseClient.asyncCas(\”1\”, 0l, \”some object\”, PersistTo.ZERO);
    for (i = 0; i < 5; i++) {
    new Thread(() -> {
    System.out.println(i + \” START – \” + new Date());
    CASValue<object> andLock = fastChangingCouchbaseClient.getAndLock(\”1\”, 30);
    String value = (String) andLock.getValue();
    fastChangingCouchbaseClient.asyncCas(\”1\”, 0l, \”some object\”, PersistTo.ZERO);
    System.out.println(i + \” END – \” + new Date());
    }).start();
    }

    Output is:

    0 START – Wed Jul 16 18:23:49 BST 2014
    1 START – Wed Jul 16 18:23:49 BST 2014
    2 START – Wed Jul 16 18:23:49 BST 2014
    3 START – Wed Jul 16 18:23:49 BST 2014
    4 START – Wed Jul 16 18:23:49 BST 2014
    0 END – Wed Jul 16 18:23:49 BST 2014
    1 END – Wed Jul 16 18:23:49 BST 2014
    2 END – Wed Jul 16 18:23:49 BST 2014
    3 END – Wed Jul 16 18:23:49 BST 2014
    4 END – Wed Jul 16 18:23:49 BST 2014

    i.e. no errors and no blocking???

    1. Hi James,

      What version of Couchbase are you using and which version of the Java client?

      Thanks,
      Don

    2. James D Bloom July 22, 2014 at 1:51 pm

      I discovered the issue above is caused by the fact that I\’m using \’0l\’ as the cas value. It turns out that if you use \’0\’ as the case value then cas is not enabled (by design). Once I changed the initial cas value from \’0\’ to \’1\’ it worked.

  3. […] Optimistic or pessimistic locking, which one should you pick? If your app needs locking, first consider using CAS(optimistic locking) before using GetL (pessimistic locking). […]

Leave a reply