Map Optimistic Locking

  Search Here
Searching XAP 6.0 Documentation

                                               

Summary: How to you write applications under the assumption that put operations may fail, if the updated object is changed by someone else since it was read.

Overview

With optimistic locking, you write your program under the assumption that a put operation has a chance of failing, if your updated object is changed by someone else since you read it. Optimistic locking offers higher concurrency and better performance than pessimistic locking. It also avoids deadlocks.

Optimistic data locking relies on the idea that data remains unmodified while it is away from the server. As a simple example, consider how you would update client details. The customer details are stored in an object, and if a client wants to update them, it will first need to get the object from the cache. The data is not locked, and other clients can have access to it simultaneously, thus ensuring a scalable system.
The problem is that while the customer details are away from the cache server, they may become stale. For example, a second client can request the same customer details, update them, and commit them back to the cache server. The first client, unaware that it is dealing with a stale copy of the data, modifies and commits the data. Obviously, with no checking mechanism to detect this conflict, the first client's changes, which commit last, will be made permanent, thus overwriting the changes made by the second client.
For optimistic locking to work effectively, you must be able to detect these update-update conflicts and to make the client aware of them so they can be dealt with appropriately.
Optimistic locking is best suited for environments with many read-only transactions, few read-update transactions, and a relatively low volume of objects that are changed.

Pessimistic locking may be less suitable for real-time systems than optimistic locking, because the cache runs best with short term transactions. Optimistic locking also has a big advantage when you may want to read a large number of objects but update only a few of them; or when it is unlikely that objects you want to work with will be updated by others. In general, optimistic locking ensures that updated objects are the most recent ones, while improving the coherency of system behavior.

Flow

  1. To enable optimistic locking, the cache URL used to generate the cache proxy should include the versioned=true property:
    IMap cache = (IMap) CacheFinder.find("jini://*/*/mySpace?versioned=true");

    Another option to activate optimistic locking is to use the IMap.setVersioned method:

    IMap cache = (IMap) CacheFinder.find("jini://*/*/mySpace");
    cache.setVersioned(true);
  2. The get operation should be done without using a transaction.
    MyData value1 = (MyData) cache.get("key");
  3. The put operation should be called with or without a transaction but under the assumption that it might throw a CacheException that encapsulates EntryVersionConflictException. In such a case, you should try and perform the get and put operations again.
    while (!completed) {
           try {
                  cache.put("key", value2);
                  completed = true;
           } catch (CacheException e) {
                  if (e.getCause() instanceof EntryVersionConflictException) {
                        value2 = (MyData) cache.get("key");
                  }
           }
    }
    The IMap.setWaitForResponse(long time_in_ms) controls the time to wait for other transaction to be completed when performing put operations with a transaction.
    The IMap.setTimeToLive(long time_in) controls the time the object will be stored in the cache.
    By default the TimToLive is set for FOREVER and the WaitForResponse is set for zero.
  4. If the IMap.put call is performed using a transaction, the IMap.put call might throw CacheTimeoutException. In such a case, you should try and perform the get and put operations again.
while (!completed) {
       try {
              cache.put("key", value2);
              completed = true;
       } catch (CacheTimeoutException e) {
              value2 = cache.get("key);
       } catch (CacheException e) {
              if (e.getCause() instanceof EntryVersionConflictException) {
                    value2 = (MyData) cache.get("key");
              }
       }
 }

Example

See example for the optimistic locking usage:

package com.gigaspaces.examples.mapoptimistic;
 
import com.j_spaces.core.client.CacheException;
import com.j_spaces.core.client.EntryVersionConflictException;
import com.j_spaces.core.client.FinderException;
import com.j_spaces.map.CacheFinder;
import com.j_spaces.map.IMap;
 
public class MapoptimisticTest {
 
       public static void main(String[] args) {
              IMap cache;
              try {
                    cache = (IMap) CacheFinder                      .find("rmi://localhost/./mySpace?versioned=true");
                    cache.clear();
                    cache.put("key", new MyData("data1"));
                    MyData value1 = (MyData) cache.get("key");
                    MyData value2 = (MyData) cache.get("key");
                    value1.data = "data2";
                    cache.put("key", value1);
                    value2.data = "data3";
                    boolean completed = false;
                    while (!completed) {
                           try {
                                  cache.put("key", value2);
                                  completed = true;
                                  System.out.println("updated key OK!");
                           } catch (CacheException e) {
                                  if (e.getCause() instanceof EntryVersionConflictException) {
                                         System.out.println(e.getCause().getMessage());
                                         System.out.println("Reading key again!");
                                         value2 = (MyData) cache.get("key");
                                  }
                           }
                    }
              } catch (FinderException e) {
                    // TODO Auto-generated catch block
                    e.printStackTrace();
              }
       }
}
 
package com.gigaspaces.examples.mapoptimistic;
 
import java.io.Serializable;
 
public class MyData implements Serializable
{
       public MyData()
       {}
       public MyData(String data)
       {
              this.data=data;
       }
       String data;
}


GigaSpaces 6.0 Documentation Contents (Current Page in Bold)

    Java

    C++

    .NET

    Middleware Capabilities

    Configuration and Management

Add GigaSpaces wiki search to your browser search engines!
(works on Firefox 2 and Internet Explorer 7)

Labels

 
(None)