Lock Items Exclusively (Pessimistic Locking)
NCache provides a locking mechanism that exclusively locks the cached data. This mechanism locks the item using the lockhandle due to which, all other users are blocked from performing any write operation on that cache item. A LockHandle is a handle associated with every locked item in the cache, which is returned by locking API.
A locked item can be fetched/updated or unlocked only when its lock handle is provided at API level. However, you should do this with care to avoid data integrity issues. Pessimistic locking is a very good approach if the goal to be achieved is data consistency.
Once lock is acquired using LockHandle
, there are two mechanisms for releasing it. Both of these mechanisms are explained below.
Time based release of locks: You can also specify lock timeout while locking a cached item. The lock timeout is the time interval after which the lock will be automatically released if no explicit call is made for releasing the lock during the timeout interval. This will prevent your data from being locked for an infinite amount of time.
Forceful release of locks: Situations can arise in distributed environments when an application which acquired the lock on a cache item terminates abruptly or an application finalizes its processing on locked data . In such a situation you would like to release all locks acquired by such an application. NCache provides an unlock API, which releases the cache item lock forcefully.
Note
It is recommended to use time based locking mechanism so that item is unlocked after the condition to be fulfilled so that resources remain acquired for minimum time.
When to Use Pessimistic Locking
Take the example discussed in the former chapter. If the same bank account is accessed by two different users at the same instance for an update operation, a conflict might occur which will lead to data inconsistency.
Pessimistic locking in this scenario will enable one user to access the account at one time. On performing the operation successfully the user unlocks the item and the control is set free which means the second user can now access the account and make amends accordingly.
Using this approach the data remains consistent and no conflict occurs.
A LockHandle is associated with an item to ensure that the particular item remains inaccessible throughout the cache.
NCache provides method calls exclusively for locking as well as numerous overloads that manipulate the locking mechanism.
Pre-Requisites for Using Pessimistic Locking
- Include the following namespace in your application:
Alachisoft.NCache.Web.Caching
Alachisoft.NCache.Runtime
- The application must be connected to cache before performing the operation.
- Cache must be running.
- Make sure that the data being added is serializable.
- To ensure the operation is fail safe, it is recommended to handle any potential exceptions within your application, as explained in Handling Failures.
Lock an Item Explicitly
You can explicitly acquire lock on an item before performing any operation. Lock
method requires a TimeSpan
to lock an item for a specified time. However, if
you do not want the acquired lock to expire simply specify TimeSpan.Zero
.
Specifying no TimeSpan
will lock an item for an infinite time.
The Lock method used in this example associates a LockHandle with a key.
Kindly ensure that the single LockHandle
is associated with a single key.
Release the lock before reusing the handle; otherwise it might lead to
inconsistency of behavior.
- If an item is already locked, then ‘false’ value will be returned but you will get the updated
LockHandle
.
Warning
Lock an item for the minimum TimeSpan for avoiding deadlock or starvation state.
Without Expiration:
The following example creates a LockHandle and then locks an item with the key
Product:1001
with no lock expiration which means the item will remain locked unless it is unlocked manually.
try
{
// Pre-Requisite: Cache is already connected
// Item is already added in the cache
// Specify the key of the item
string key = $"Product:1001";
//Create a new lock Handle
LockHandle lockHandle = null;
//Lock the item by specifying the key and LockHandle
//NoLockExpiration means that item is added in the cache without lock expiration
bool lockAcquired = cache.Lock(key, Cache.NoLockExpiration, out lockHandle);
if (lockAcquired == true)
{
// Item is successfully locked
}
else
{
// Item is not locked because either:
// Key is not present in the cache
// Item is already locked with a different lockHandle
}
}
catch (OperationFailedException ex)
{
// NCache specific exception can occur due to:
// Connection failures
// Operation performed during state transfer
// Operation timeout
}
catch (Exception ex)
{
// Any generic exception like ArgumentNullException or ArgumentException
}
With Expiration:
The following example creates a LockHandle and then locks an item with the key
Product:1001
for a timespan of 10 seconds which means item will be unlocked automatically after 10 seconds.
try
{
// Pre-Requisite: Cache is already connected
// Item is already added in the cache
// Specify the key of the item
string key = $"Product:1001";
//Create a new LockHandle
LockHandle lockHandle = null;
// Specify time span of 10 seconds for which the item remains locked
TimeSpan lockSpan = TimeSpan.FromSeconds(10);
// Lock the item for a time span of 10 seconds
bool lockAcquired = cache.Lock(key, lockSpan, out lockHandle);
// Verify if the item is locked successfully
if (lockAcquired == true)
{
// Item has been successfully locked
}
else
{
// Key does not exist
// Item is already locked with a different LockHandle
}
// Verify that the lock is released automatically after this time period.
}
catch (OperationFailedException ex)
{
// NCache specific exception can occur due to:
// Connection failures
// Operation performed during state transfer
// Operation timeout
}
catch (Exception ex)
{
// Any generic exception like ArgumentNullException or ArgumentException
}
Recommendation: To ensure the operation is fail safe, it is recommended to handle any potential exceptions within your application, as explained in Handling Failures.
Lock an Item during Get Operation
An item can be locked during the process of its retrieval from the cache. This means that the item will be inaccessible for others unless you release it. In case of mismatch of key, null value is returned.
If an item is not locked and
acquirelock
is set true then you will get the item along with the LockHandle.If an item is locked and
acquirelock
is set false and if you pass incorrect or new empty LockHandle thennull
value is returned but you will get the LockHandle which was used to lock the item previously.If an item is locked and
acquirelock
is set false and correct LockHandle is passed which was previously used to lock the item, then you will get the value.
Warning
Lock an item for the minimum TimeSpan for avoiding deadlock or thread starvation.
Without Expiration:
In this example a key and LockHandle
is specified to fetch
the cached object and lock it. You need to specify “true” if you need to acquire
the lock. Here the item is locked with no lock expiration which means that the item is locked for an unlimited time and will remain locked unless unlocked manually.
try
{
// Pre-Requisite: Cache is already connected
// Item is already added in the cache
// Specify the key of the item
string key = $"Product:1001";
//Create a new LockHandle
LockHandle lockHandle = null;
// Get and lock the item because acquirelock is set true
// Locks the item without lock expiry which means lock will
// not be released automatically
object result = cache.Get(key, Cache.NoLockExpiration, ref lockHandle, true);
if (result != null)
{
// Perform operation
}
}
catch (OperationFailedException ex)
{
// NCache specific exception can occur due to:
// Connection failures
// Operation performed during state transfer
// Operation timeout
}
catch (Exception ex)
{
// Any generic exception like ArgumentNullException or ArgumentException
}
With Expiration:
In this example a key and LockHandle
is specified to fetch
the cached object and lock it. You need to specify “true” if you need to acquire
the lock. Here the item is locked with an expiration of 10 seconds which means item will be unlocked automatically after 10 seconds.
try
{
// Pre-Requisite: Cache is already connected
// Item is already added in the cache
// Specify the key of the item
string key = $"Product:1001";
//Create a new LockHandle
LockHandle lockHandle = null;
// Specify time span of 10 seconds for which the item remains locked
TimeSpan lockSpan = TimeSpan.FromSeconds(10);
// Lock the item for a time span of 10 seconds
object result = cache.Get(key, lockSpan, ref lockHandle, true);
// Verify if the item is locked successfully
if (result != null)
{
// Item has been successfully locked
}
else
{
// Key does not exist
// Item is already locked with a different LockHandle
}
// Verify that the lock is released automatically after this time period.
}
catch (OperationFailedException ex)
{
// NCache specific exception can occur due to:
// Connection failures
// Operation performed during state transfer
// Operation timeout
}
catch (Exception ex)
{
// Any generic exception like ArgumentNullException or ArgumentException
}
Recommendation: To ensure the operation is fail safe, it is recommended to handle any potential exceptions within your application, as explained in Handling Failures.
Release Lock with Update Operation
While updating an item, you can release the lock allowing others to use the
cached data. In order to successfully release the locked item, you will need to
specify the LockHandle
initially used to lock the item.
LockHandle
should be the same which was used initially to lock the item otherwise you will get the an exception message sayingItem is Locked
.If
releaseLock
is set false still you have to pass the correctLockhandle
to update the item.If an item is not locked then
Lockhandle
andreleaseLock
are of no use and they are ignored.
The following example locks an item in the cache and then gets the item using the lockHandle. The item is then updated and then reinserted in the cache using the Insert API.
try
{
// Pre-Requisite: Cache is already connected
// Item is already added in the cache
// Specify the key of the item
string key = $"Product:1001";
// Initialize the lockHandle
LockHandle lockHandle = null;
// Get and lock the item because acquirelock is set true
// Locks the item without lock expiry which means lock will
// not be released automatically
CacheItem item = cache.GetCacheItem(key, Cache.NoLockExpiration, ref lockHandle, true);
// Update the unitsinstock for the product
product.UnitsInStock = 200;
item.Value = product;
// Item is already locked with a LockHandle
// Update the item and release the lock as well since releaseLock is set true
// Make sure that the LockHandle matches with the already added LockHandle
cache.Insert(key, item, lockHandle, true);
}
catch (OperationFailedException ex)
{
// NCache specific exception
if (ex.Message.Contains("Item is locked."))
{
// If Item is already locked with a different LockHandle
}
else
{
// NCache specific exception can occur due to:
// Connection failures
// Operation performed during state transfer
// Operation timeout
}
}
catch (Exception ex)
{
// Any generic exception like ArgumentNullException or ArgumentException
}
Recommendation: To ensure the operation is fail safe, it is recommended to handle any potential exceptions within your application, as explained in Handling Failures.
Release Lock Explicitly
In order to release lock explicitly on a previously locked cached item; you will
need to specify the LockHandle
initially used to lock the item.
If LockHandle
is not saved, you can also use another overload of Unlock()
which only takes the key to unlock the item.
Note
If invalid Lockhandle
is passed then no exception will be thrown but the item will remain locked.
The following example gets an item already locked using the LockHandle
and then unlocks it using the Unlock
API using the Lockhandle
saved before.
try
{
// Pre-Requisite: Cache is already connected
// Item is already added in the cache
// Specify the key of the item
string key = $"Product:1001";
//Create a new LockHandle
LockHandle lockHandle = null;
object result = cache.Get(key, Cache.NoLockExpiration, ref lockHandle, true);
// Make sure that the item is already locked and the saved LockHandle is used
// Unlock locked item using saved LockHandle
cache.Unlock(key, lockHandle);
// Item is successfully unlocked
}
catch (OperationFailedException ex)
{
// NCache specific exception can occur due to:
// Connection failures
// Operation performed during state transfer
// Operation timeout
}
catch (Exception ex)
{
// Any generic exception like ArgumentNullException or ArgumentException
}
Recommendation: To ensure the operation is fail safe, it is recommended to handle any potential exceptions within your application, as explained in Handling Failures.
Warning
NCache will ignore the locks if other overloads of Get
, Insert
and Remove
methods are called which do not take or use LockHandle
.
Remove Item with LockHandle
The remove method is a basic method which removes the key from cache and returns the removed object to the client. If a custom object is added to the cache, the remove method will return Object.
LockHandle
should be same which was used initially to lock the item otherwise you will get the an exception message sayingItem is locked
.If an item is not locked, then
Lockhandle
is of no use and its validity is not checked.
The following example gets an item which is previously locked using the LockHandle
and then removes it by the saved LockHandle
from the cache using the Remove API.
Tip
You can monitor/verify removal:
- "Cache Count" Counter in NCache Monitor or PerfMon Counters
- Using cache.Contains() after expiration interval has elapsed
- Using cache.Count before and after specifying expiration
try
{
// Pre-Requisite: Cache is already connected
// Item is already added in the cache
// Specify the key of the item
string key = $"Product:1001";
// Initialize the lockHandle
LockHandle lockHandle = null;
// Get the item using the lockHandle
cache.Get(key, Cache.NoLockExpiration, ref lockHandle, true);
// Removing locked item using saved lockHandle.
object result = cache.Remove(key, lockHandle);
// Check if item is successfully removed
if (result != null)
{
if (result is Product)
{
Product product = (Product)result;
}
}
}
catch (OperationFailedException ex)
{
// NCache specific exception
if (ex.Message.Contains("Item is locked."))
{
// If Item is already locked with a different LockHandle
}
else
{
// NCache specific exception can occur due to:
// Connection failures
// Operation performed during state transfer
// Operation timeout
}
}
catch (Exception ex)
{
// Any generic exception like ArgumentNullException or ArgumentException
}
Recommendation: To ensure the operation is fail safe, it is recommended to handle any potential exceptions within your application, as explained in Handling Failures.
Delete Item with LockHandle
The delete
method is a basic method which deletes the key from the cache and does not return anything. Using this method
the item is deleted from the cache using the LockHandle
.
LockHandle
should be same which was used initially to lock the item otherwise you will get the an exception message sayingItem is locked
.If item is not locked, then
Lockhandle
is of no use and its validity is not checked.
The following example gets an item which is previously locked using the LockHandle
and then deletes it from the cache by Lockhandle
using the
Delete method.
Tip
You can monitor/verify removal:
- "Cache Count" Counter in NCache Monitor or PerfMon Counters
- Using cache.Contains() after expiration interval has elapsed
- Using cache.Count before and after specifying expiration
try
{
// Pre-Requisite: Cache is already connected
// Item is already added in the cache
// Specify the key of the item
string key = $"Product:1001";
// Initialize the lockHandle
LockHandle lockHandle = null;
// Get the item using the lockHandle
cache.Get(key, Cache.NoLockExpiration, ref lockHandle, true);
// Delete locked item using saved lockHandle.
cache.Delete(key, lockHandle);
}
catch (OperationFailedException ex)
{
// NCache specific exception
if (ex.Message.Contains("Item is locked."))
{
// If Item is already locked with a different LockHandle
}
else
{
// NCache specific exception can occur due to:
// Connection failures
// Operation performed during state transfer
// Operation timeout
}
}
catch (Exception ex)
{
// Any generic exception like ArgumentNullException or ArgumentException
}
Recommendation: To ensure the operation is fail safe, it is recommended to handle any potential exceptions within your application, as explained in Handling Failures.
Warning
NCache will ignore the locks if other overloads of Get
, Insert
and Remove
methods are called which do not take or use LockHandle
.
Special Consideration while Using API Locking
NCache provides a set of APIs with and without LockHandle
to fetch/update the
cache item. API's without a LockHandle
ignore item locking. So you should use
all locking APIs for data manipulation. For example, if an item is locked and
you make an update API call which does not take the LockHandle
as input
parameter, then the item will be updated in the cache irrespective of its
locking state.
Important
When using a locking feature, you should only use API
calls which take LockHandle
as parameters. API's which do not take lock
handles can be used but should be done so with a lot of care so that it does not
affect data integrity.
Note
In case of eviction/expiration, NCache ignores locks which means that a locked item can be removed as a result of expiration or eviction.
Topology Wise Behavior
- Mirrored and Replicated Topology
In mirror topology, for all lock operations lock is acquired on Active node and same LockHandle is then replicated to Passive node so that when Passive becomes Active, item will remain locked. Similarly unlock call is also replicated to passive node to unlock the item from passive node.
In replicated topology, client is connected to one node and for all lock operations LockHandle is generated which receives client lock operation and then same LockHandle will be replicated to all other nodes for data consistency, similarly unlock operation will also be replicated to all other nodes.
- Partitioned and Partitioned Replica Topology
In partitioned topology, LockHandle is generated and exists on the same node which contains the item and during state transfer LockHandle info is also transferred along with the item in case item moves to another node.
In partitioned-replica topology, LockHandle is generated on the active node which contains the item and same LockHandle is then replicated to its replica for data consistency and during state transfer LockHandle info is also transferred along with the item in case item moves to another node.
- Client Cache
In Client Cache, all lock based operations are directly performed at clustered cache which means that LockHandle is generated and stored at clustered cache. No locking related information is maintained at Client Cache.
See Also
Sync Cache With Database
Lock Items with Cache Item Versioning (Optimistic Locking)
Search Cache with SQL