With the development of high transaction distributed applications, distributed caching solutions have become highly desirable to achieve performance scalability. NCache is a good choice as an in-memory distributed data store since it provides linear scalability and high availability.
So far, so good, but how to ensure data integrity in such shared environments is a big deal. Since two or more clients can access and alter the same data simultaneously in your application, the result can be inconsistent data. Once data integrity is violated, the data in the cache becomes virtually useless.
In this blog post, I will explain how this problem occurs and how NCache saves you with its distributed locking feature.
Data Integrity Problems Without Locking
To understand the data integrity problem in detail, we consider the example of an online E-Commerce store where sellers upload their products while customers view and place orders for those products to purchase.
Suppose a customer wants to view a certain product and the seller of that product wants to update its price. Now if a customer is viewing a product at a higher price and the seller adds a discount that the user cannot see yet, the user ends up purchasing the product at a higher price.
The scenario is shown in Figure 1.
- Client 1 reads the details of the product in the cache.
- Client 2 also reads the product data. It is correct.
- Client 1 now updates product details in the cache.
- Client 2 also updates the product details in the cache.
- Updates made by client 1 are lost.
Let’s see how NCache resolves this problem.
NCache Distributed Locking
NCache provides a flexible way of securing your data according to your business needs by allowing you to lock your data. One user takes control of a chunk of data and updates it. Meanwhile, no other user can manipulate that data.
Based on your application scenario, you can choose either of these NCache locking mechanisms:
- Pessimistic locking (transactional or exclusive locks): Locks an item exclusively which makes it inaccessible for other users.
- Optimistic locking (lock through item versioning): Uses item versioning which makes an item accessible for other users.
Pessimistic Locking for Sensitive Data
You should use the pessimistic locking strategy when the data to be updated in your application is sensitive. You can use a LockHandle to acquire an explicit lock on your data. Once you are done, the lock is released.
Now, we see that how NCache pessimistic locking solves the data consistency problem in our example. You can acquire an exclusive lock on the product to update meanwhile, no other user can access that product. This is illustrated in figure 2.
- Client 1 acquires lock over the product and starts adding and updating the product details
- Client 2 requests to read product details, which is denied, and Client 2 has to wait till the lock acquired is released
- Once the lock is released, Client 2 can carry on its regular operations on the product.
It is important to note that, NCache supports two sets of APIs: with and without locking. If the user wants to use pessimistic locking, then APIs with locking parameters should be used everywhere in applications where strong data consistency is the requirement. Let’s see how you can do it with NCache locking feature. In the following code segment, first, a new LockHandle is created and then the timespan is set to specify the time period for which the lock will be acquired. This LockHandle works as a lock ID to identify the lock. A lock is then acquired using the Get method with a key and LockHandle.
Now you can perform your business operations and manually release the lock or wait for the timespan to end.
// Pre-condition: Cache is already connected
// Create a new lock handle to fetch an Item using locking
LockHandle lockHandle = new LockHandle();
// Timespan for which lock is to be taken
TimeSpan timeSpan = TimeSpan.FromSeconds(5);
// Get item from the cache and lock it
var result = cache.Get(key, true, timeSpan, ref lockHandle);
// Verify if the item is locked successfully
if (result != null)
// Item has been successfully locked
// Key does not exist
// Item is already locked with a different LockHandle
//Unlock item in cache manually
Here, Get API with lock parameters immediately returns without waiting for lock acquisition. You need to make explicit reties to acquire a lock on items in case you fail in the first place.
You can also acquire a lock using the Lock method which associates a LockHandle with a key. NCache provides various ways to acquire/release an explicit lock which enables flexible locking. If you intend to implement NCache locking, using the NCache sample application for item locking on GitHub will be helpful.
Optimistic Locking for Data Availability
Pessimistic locking is great but it may not be an optimal approach when response time is critical for your application. This is where optimistic locking comes in handy.
NCache optimistic locking uses cache item versioning to overcome the thread starvation caused in the case of explicit lock. Hence, you can work on a version of the cached item which is incremented with every update to that item. NCache keeps track of the item version and you don’t need to worry about data consistency.
Figure 3 shows how it’s done:
- Client 1 adds details about the product and the CacheItemVersion is set to v1.
- Client 2 reads updated product details with CacheItemVersion v1.
- Client 1 again alters the details due to which CacheItemVersion is incremented and it becomes v2.
- Now when Client 2 tries to update details using old CacheItemVersion v1, the operation fails with CacheItemVersion
- Client 2 gets the latest CacheItemVersion
- Client 2 updates the product details using CacheItemVersion Now the operation is performed successfully and CacheItemVersion is incremented by 1 automatically.
This ensures all users have updated CacheItemVersion at all times and no user is refused access to any product in the store.
The following code shows how to do it:
// Specify the key of the cacheItem
string key = "Product:1001";
// Initialize the cacheItemVersion
CacheItemVersion version = null;
// Get the cacheItem previously added in the cache with the version
CacheItem cacheItem = cache.GetCacheItem(key, ref version);
// If result is not null
if (cacheItem != null)
// CacheItem is retrieved successfully with the version
var prod = cacheItem.GetValue();
prod.Discount = 0.5;
// Create a new cacheItem with updated value
var updateItem = new CacheItem(prod);
//Set the itemversion. This version is used to compare the
// item version of the cached item
updateItem.Version = version;
//Insert call will fail with LockingException, if cache contains a newer version of the cache item.
//In case of LockingException, we can fetch the latest cache item from cache and update it
// If it matches, the insert is successful, otherwise it fails
Why Use NCache Locking
Secure and consistent data is crucial for today’s businesses and it would be a shame if something as simple as multi-user transactions damages your data integrity.
NCache ensures the integrity and consistency of your data in highly distributed environments with utmost flexibility. Based on your application scenario, you can adopt different locking mechanisms in various ways provided by NCache. Eventually, you will have concurrency without any data inconsistency!