Locking strategies

Locking strategies include pessimistic, optimistic, and none. To choose a locking strategy, you must consider issues such as the percentage of each type of operations you have, whether you use a loader, and so on.

Locks are bound by transactions. You can specify the following locking settings:
[Java programming language only]No locking
Running without the locking setting is the fastest. If you are using read-only data, then you might not need locking.
Restriction: BackingMaps configured to use a no locking strategy cannot participate in a multi-partition transaction.
Deprecated featureDeprecated: Multi-partition transaction operations are deprecated.
[.net programming language only][Java programming language only]Pessimistic locking
Acquires locks on entries, then and holds the locks until commit time. This locking strategy provides good consistency at the expense of throughput.
[Java programming language only]Optimistic locking
Takes a before image of every record that the transaction touches and compares the image to the current entry values when the transaction commits. If the entry values change, then the transaction rolls back. No locks are held until commit time. This locking strategy provides better concurrency than the pessimistic strategy, at the risk of the transaction rolling back and the memory cost of making the extra copy of the entry.
[Java programming language only]Optimistic no versioning locking
[Java programming language only]This locking strategy allows you to disable version control. This is important because near cache is only enabled if you are doing Optimistic locking. With the current implementation, you need a plug-in or callback handler to handle version control. However, using the OPTIMISTIC_NO_VERSIONING locking strategy to disable version control on the client and only enable it on the server, is an additional performance savings.
Important: If you are using a client application with WebSphere® eXtreme Scale Client for .NET, only pessimistic locking is supported.

Lock manager

When either a PESSIMISTIC or an OPTIMISTIC or a OPTIMISTIC_NO_VERSIONING locking strategy is used, a lock manager is created for the BackingMap. The lock manager uses a hash map to track entries that are locked by one or more transactions. If many map entries exist in the hash map, more lock buckets can result in better performance. The risk of Java™ synchronization collisions is lower as the number of buckets grows. More lock buckets also lead to more concurrency. The previous examples show how an application can set the number of lock buckets to use for a given BackingMap instance.

[.net programming language only][Java programming language only]

Pessimistic locking

The PESSIMISTIC lock strategy acquires locks for cache entries and should be used when data is changed frequently. Any time a cache entry is read, a lock is acquired and conditionally held until the transaction completes. The duration of some locks can be tuned using transaction isolation levels for the session.

Use the pessimistic locking strategy for read and write maps when other locking strategies are not possible. When an ObjectGrid map is configured to use the pessimistic locking strategy, a pessimistic transaction lock for a map entry is obtained when a transaction first gets the entry from the BackingMap. The pessimistic lock is held until the application completes the transaction. Typically, the pessimistic locking strategy is used in the following situations:
  • When the BackingMap is configured with or without a loader and versioning information is not available.
    Restriction: BackingMaps that are configured with a Loader plug-in can read but cannot write to the map in a multi-partition transaction.
    Deprecated featureDeprecated: Multi-partition transaction operations are deprecated.
  • When the BackingMap is used directly by an application that needs help from the eXtreme Scale for concurrency control.
  • When versioning information is available, but update transactions frequently collide on the backing entries, resulting in optimistic update failures.

The pessimistic locking strategy has the greatest impact on performance and scalability. Therefore, use this strategy only for read and write maps when other locking strategies are not viable. For example, these situations might include when optimistic update failures occur frequently, or when recovery from optimistic failure is difficult for an application to handle.

When you use pessimistic locking, you can use lock methods to lock data, or keys, without returning any data values. For a list of the methods and what kind of locks they acquire, see Lock types.

[Java programming language only]

Optimistic locking

The default lock strategy is OPTIMISTIC. Use optimistic locking when data is changed infrequently. Locks are only held for a short duration while data is being read from the cache and copied to the transaction. When the transaction cache is synchronized with the main cache, any cache objects that have been updated are checked against the original version. If the check fails, then the transaction is rolled back and an OptimisticCollisionException exception results.

The optimistic locking strategy assumes that no two transactions might attempt to update the same map entry while the transactions are running concurrently. The lock is not held for the lifecycle of the transaction because it is unlikely that more than one transaction might update the map entry concurrently. The optimistic locking strategy is typically used in the following situations:
  • When a BackingMap is configured and versioning information is available. The BackingMap can be configured with or without a loader.
    Restriction: BackingMaps that are configured with a Loader plug-in can read but cannot write to the map in a multi-partition transaction.
    Deprecated featureDeprecated: Multi-partition transaction operations are deprecated.
  • When a BackingMap has mostly transactions that are read operations. Insert, update, or remove operations on map entries do not occur often on the BackingMap.
  • When a BackingMap is inserted, updated, or removed more frequently than it is read, but transactions rarely collide on the same map entry.

Like the pessimistic locking strategy, the methods on the ObjectMap interface determine how eXtreme Scale automatically attempts to acquire a lock mode for the map entry that is being accessed. However, the following differences between the pessimistic and optimistic strategies exist:

  • Like the pessimistic locking strategy, an S lock mode is acquired by the get and getAll methods when the method is called. However, with optimistic locking, the S lock mode is not held until the transaction is completed. Instead, the S lock mode is released before the method returns to the application. The purpose of acquiring the lock mode is so that eXtreme Scale can ensure that only committed data from other transactions is visible to the current transaction. After eXtreme Scale has verified that the data is committed, the S lock mode is released. At commit time, an optimistic versioning check is performed to ensure that no other transaction has changed the map entry after the current transaction released its S lock mode. If an entry is not fetched from the map before it is updated, invalidated, or deleted, the eXtreme Scale run time implicitly fetches the entry from the map. This implicit get operation is performed to get the current value at the time the entry was requested to be modified.
  • Unlike pessimistic locking strategy, the getForUpdate and getAllForUpdate methods are handled exactly like the get and getAll methods when the optimistic locking strategy is used. That is, an S lock mode is acquired at the start of the method and the S lock mode is released before returning to the application.

All other ObjectMap methods are handled the same as the pessimistic locking strategy. When the commit method is called, an X lock mode is obtained for any map entry that is inserted, updated, removed, touched, or invalidated. The X lock mode is held until the transaction completes commit processing.

The optimistic locking strategy assumes that no concurrently running transactions attempt to update the same map entry. Because of this assumption, the lock mode does not need to be held for the life of the transaction because it is unlikely that more than one transaction might update the map entry concurrently. However, because a lock mode was not held, another concurrent transaction might potentially update the map entry after the current transaction has released its S lock mode.

To handle this possibility, eXtreme Scale gets an X lock at commit time and performs an optimistic versioning check to verify that no other transaction has changed the map entry after the current transaction read the map entry from the BackingMap. If another transaction changes the map entry, the version check fails and an OptimisticCollisionException exception occurs. This exception forces the current transaction to be rolled back and the application must try the entire transaction again. The optimistic locking strategy is useful when a map is mostly read and it is unlikely that updates for the same map entry might occur.

Restriction:

When you use optimistic locking with COPY_TO_BYTES, you might experience ClassNotFoundException exceptions during common operations, such as invalidating cache entries. These exceptions occur because the optimistic locking mechanism must call the equals() method of the cache object to detect any changes before the transaction is committed. To call theequals() method, the container server must be able to deserialize the cached object, which means that eXtreme Scale must load the object class.

To resolve these exceptions, you can package the cached object classes so that the container server can load the classes in stand-alone environments. Therefore, you must put the classes in the class path.

If your environment includes the OSGi framework, then package the classes into a fragment of the objectgrid.jar bundle. If you are running eXtreme Scale servers in the Liberty, package the classes as an OSGi bundle, and export the Java packages for those classes. Then, install the bundle by copying it into the grids directory along with your objectgrid.xml file.

In WebSphere Application Server, package the classes in the application or in a shared library that the application can access.

Alternatively, you can use custom serializers that can compare the byte arrays that are stored in eXtreme Scale to detect any changes.

[Java programming language only]

Optimistic no versioning

You can enable OPTIMISTIC_NO_VERSIONING locking either through the client override XML file or programmatically. See the following examples of both approaches:

Client override XML file example

<objectGrid name="lockStrategyGrid">
            <backingMap name="opt_with_noversion" lockStrategy="OPTIMISTIC_NO_VERSIONING"/>
            <backingMap name="opt_with_none" lockStrategy="NONE"/>
            <backingMap name="optnoversion_with_opt" lockStrategy="OPTIMISTIC"/>
            <backingMap name="optnoversion_with_none" lockStrategy="NONE"/>                            
        </objectGrid>

Programmatic example

ObjectGridConfiguration lsConfig = ObjectGridConfigFactory.createObjectGridConfiguration("lockStrategyGrid");
	        BackingMapConfiguration oMapWithOVConfig = ObjectGridConfigFactory.createBackingMapConfiguration("opt_with_noversion");
	        oMapWithOVConfig.setLockStrategy(LockStrategy.OPTIMISTIC_NO_VERSIONING);
	        lsConfig.addBackingMapConfiguration(oMapWithOVConfig);
[Java programming language only]

No locking

If locking is not required because the data is never updated or is only updated during quiet periods, you can disable locking by using the NONE lock strategy. This strategy is very fast because a lock manager is not required. The NONE lock strategy is ideal for look-up tables or read-only maps.

When a BackingMap is configured to use no locking strategy, no transaction locks for a map entry are obtained.
Restriction: BackingMaps configured to use a no locking strategy cannot participate in a multi-partition transaction.
Deprecated featureDeprecated: Multi-partition transaction operations are deprecated.

Using no locking strategy is useful when an application is a persistence manager such as an Enterprise JavaBeans (EJB) container or when an application uses Hibernate to obtain persistent data. In this scenario, the BackingMap is configured without a loader and the persistence manager uses the BackingMap as a data cache. In this scenario, the persistence manager provides concurrency control between transactions that are accessing the same Map entries.

WebSphere eXtreme Scale does not need to obtain any transaction locks for concurrency control. This situation assumes that the persistence manager does not release its transaction locks before updating the ObjectGrid map with committed changes. If the persistence manager releases its locks, then a pessimistic or optimistic lock strategy must be used. For example, suppose that the persistence manager of an EJB container is updating an ObjectGrid map with data that was committed in the EJB container-managed transaction. If the update of the ObjectGrid map occurs before the persistence manager transaction locks are released, then you can use the no lock strategy. If the ObjectGrid map update occurs after the persistence manager transaction locks are released, then you must use either the optimistic or pessimistic lock strategy.

Another scenario where no locking strategy can be used is when the application uses a BackingMap directly and a Loader is configured for the map. In this scenario, the loader uses the concurrency control support that is provided by a relational database management system (RDBMS) by using either Java database connectivity (JDBC) or Hibernate to access data in a relational database. The loader implementation can use either an optimistic or pessimistic approach. A loader that uses an optimistic locking or versioning approach helps to achieve the greatest amount of concurrency and performance. For more information about implementing an optimistic locking approach, see the OptimisticCallback section in Configuring database loaders. If you are using a loader that uses pessimistic locking support of an underlying backend, you might want to use the forUpdate parameter that is passed on the get method of the Loader interface. Set this parameter to true if the getForUpdate method of the ObjectMap interface was used by the application to get the data. The loader can use this parameter to determine whether to request an upgradeable lock on the row that is being read. For example, DB2® obtains an upgradeable lock when an SQL select statement contains a FOR UPDATE clause. This approach offers the same deadlock prevention that is described in Pessimistic locking.