• Products
  • Solutions
  • Customers
  • Resources
  • Company
  • Pricing
  • Download
Try Playground
  • Client Side API Programming
  • Locks
  • Optimistic Locking
Show / Hide Table of Contents
  • Programmer's Guide
  • Setting Up Development Environment
    • .NET
      • Client API Prerequisites
      • Server-side API Prerequisites
    • Java
      • Client API Prerequisites
      • Server-side API Prerequisites
    • Python
      • Client API Prerequisites
    • Node.js
      • Client API Prerequisites
  • Client Side API Programming
    • Error Handling
    • Troubleshooting
    • Cache Keys and Data
    • How to Connect to Cache
    • Basic Operations - An Overview
      • Add Data
      • Update/Insert Data
      • Retrieve Data
      • Remove Data
    • Groups
      • Overview
      • Add/Update Data with Groups
      • Retrieve Data with Groups
      • Remove Data with Group
      • Search Group Data Using SQL
      • Delete Group Data Using SQL
    • Tags
      • Overview
      • Add/Update Data with Tags
      • Retrieve Data with Tags
      • Remove Data with Tags
      • Search Tag Data Using SQL
      • Delete Tag Data Using SQL
    • Named Tags
      • Overview
      • Add/Update Data with Named Tags
      • Remove Data with Named Tags
      • Search Data with Named Tags Using SQL
      • Delete Data with Named Tags Using SQL
    • Expirations
      • Overview
      • Absolute Expiration
      • Sliding Expiration
    • Data Dependency
      • Key Dependency
      • Multi-Cache Dependency
    • Dependency on Database
      • SQL Server
      • Oracle
      • OleDB with Polling
      • CLR Procedures in SQL Server
    • Dependency on External Source
      • File Dependency
      • Custom Dependency
      • Aggregate Dependency
    • Locks
      • Types of Locking
      • Pessimistic Locking
      • Optimistic Locking
    • SQL Query
      • Overview
      • Define Indexes Programmatically
      • Query with ExecuteReader and ExecuteScalar
      • Delete Data with ExecuteNonQuery
      • SQL Reference
    • LINQ Query
      • Overview
      • LINQ Query for Objects
      • LINQ Reference
    • Data Structures
      • Overview
      • List
      • Queue
      • Set
      • Dictionary
      • Counter
      • Invalidation Attributes
      • Searchable Attributes
      • Query on Data Structures
      • Remove from Data Structure
    • Events
      • Cache Level Events
      • Item Level Events
      • Management Level Events
    • Pub/Sub Messaging
      • Overview
      • Topics
      • Publish Messages
      • Subscribe to a Topic
      • Pub/Sub Events
    • Continuous Query
      • Overview
      • Use Continuous Query
    • Stream Processing
      • Add/Update Stream Data
      • Retrieve Stream Data
    • JSON
      • Overview
      • Use JSON Objects
      • Query JSON Data
    • Security API
      • Login with Credentials
    • Management API
    • Clear Cache
    • Error Logging
    • Location Affinity
  • Server-side API Programming
    • Loader and Refresher
      • Overview
      • Implement Loader and Refresher
      • Components of Loader/Refresher
    • Data Source Providers
      • Read-through
        • Implement Read-through
        • Use Read-through
      • Write-through
        • Implement Write-through
        • Use Write-through
        • Use Write-behind
    • Custom Dependency
      • Implement Extensible Dependency
      • Implement Bulk Extensible Dependency
      • Implement Notify Extensible Dependency
    • Bridge Conflict Resolver
    • Entry Processor
      • Overview
      • Implement Entry Processor
    • MapReduce
      • Overview
      • Implement MapReduce
      • Use MapReduce
    • MapReduce Aggregator
      • Overview
      • Implement and Use Aggregator
    • Compact Serialization
  • Client Side Features
    • ASP.NET Core Caching
      • Session Storage
        • Session Provider
        • IDistributedCache
        • Sessions Usage
        • Multi-site Session Provider
        • Session Sharing with ASP.NET
      • SignalR
        • NCache Extension for SignalR Core
      • Response Caching
        • Configure and Use
        • Configure with IDistributedCache
      • Data Caching
        • NCache API
        • IDistributedCache API
      • Data Protection Provider
        • Configure
    • Java Web App Caching
      • Web Sessions
        • Overview
        • Configure App
          • Add Maven Dependencies
          • Deploy Application
        • Multi-site Sessions
    • Node.js App Caching
      • Web Sessions
    • ASP.NET Caching Benefits and Overview
      • ASP.NET Session State Provider Properties
      • Multi-region ASP.NET Session State Provider Configuration
      • Session Sharing between ASP.NET and ASP.NET Core
      • ASP.NET SignalR Backplane
        • NCache Extension for SignalR
      • ASP.NET View State Caching
        • View State Content Optimization Configuration
        • Group View State with Sessions
        • Limit View State Caching
        • Page Level Grouping
      • ASP.NET Output Cache
        • Output Caching Provider Overview
        • Output Cache with Custom Hooks
  • .NET Third Party Integrations
    • Entity Framework (EF) Core
      • Installation
      • Configure
      • EF Core Extension Methods
        • Extension Methods
        • Cache Handle
        • Caching Options
        • Query Deferred API
      • Logging in EF Core
    • Entity Framework EF 6
      • EF Second Level Cache
      • EF Caching Resync Provider
      • EF Caching Configuration File
    • NHibernate
      • Second Level Cache
      • Query Caching
      • Synchronize Database with Cache
    • Debug NCache Providers in Visual Studio
  • Java Third Party Integrations
    • Hibernate
      • Second Level Cache
      • Configure Cacheable Objects and Regions
      • Configure Application
      • Query Caching
    • Spring
      • Overview
      • Use NCache with Spring
        • Configure Generic Provider
        • Configure JCache Spring Caching Provider
        • Configure Caching Declaration
        • Configure Spring Sessions
    • JCache API
      • CRUD Operations
      • Expiration
      • Events
  • Third-Party Wrappers
    • AppFabric to NCache Migration
      • AppFabric API vs. NCache API
      • Configuration Differences Between AppFabric and NCache
      • Migrating from an AppFabric Application to NCache
    • Redis to NCache Migration
      • Redis to NCache Migration
    • Memcached Wrapper
      • Memcached Gateway Approach
      • Memcached Client Plugin for .NET

Locking with Cache Item Versioning (Optimistic Locking)

While Pessimistic Locking is a very helpful approach, there is a limitation involved in using it, an item cannot be used unless at least one operation is performed on it completely. This means that the item remains locked till one task is performed completely on the item. This can cause thread starvation if an item remains locked for a long time.

Note

This feature is also available in NCache Professional.

This is where Optimistic Locking comes in handy since NCache uses cache item versioning. The CacheItemVersion is a property associated with every cache item. It is a numeric value that represents the version of the cached item, which increments itself by one with every update to an item. This property allows you to track whether any change occurs in an item or not. When you fetch an item from the cache, you also fetch its current version in the cache. For read-intensive applications, Optimistic Locking is preferred over Pessimistic Locking.

When to Use Optimistic Locking

In the previous example, we had a single bank account used by two users simultaneously. Let's suppose one of the users acquired the lock for performing a deposit transaction on the bank account. User 2 is waiting for User 1 to free the lock for him to make the withdrawal transaction. Consider that User 1 goes to an unstable state due to network connectivity issues without setting the lock free. User 2 keeps waiting for him to release the lock without the knowledge of any connectivity issues so he goes to the state of starvation till the first user releases the lock.

To avoid this kind of problem, Optimistic Locking is a useful solution. Using this kind of Locking, if User 1 wants to update the account, he can update the account without Locking it and the item version will be updated accordingly. Now when User 2 wants to update the data, he will get the updated version based on the item version and this would make sure no data integrity issues occur. If any user operates on the data with the old item version, the operation will fail considering it has an outdated item version.

The CacheItemVersion adds an additional dimension to the development of the application using NCache. Optimistic concurrency can be achieved in applications by NCache Item Versioning. When any item is added to the cache, the cache item version returns to the cache client. This value denotes the number of updates performed on particular data. With every update, the value of the item version increments.

Prerequisites

  • .NET
  • Java
  • Python
  • Node.js
  • Legacy API
  • To learn about the standard prerequisites required to work with all NCache client-side features, please refer to the given page on Client-Side API Prerequisites.
  • For API details, refer to: Add, ICache, CacheItem, CacheItemVersion, Contains, Count, GetIfNewer, Insert, Remove.
  • To learn about the standard prerequisites required to work with all NCache client-side features, please refer to the given page on Client-Side API Prerequisites.
  • For API details, refer to: add, Cache, CacheItem, CacheItemVersion, contains, getCount, getIfNewer, insert, remove.
  • To learn about the standard prerequisites required to work with all NCache client-side features, please refer to the given page on Client-Side API Prerequisites.
  • For API details, refer to: Cache, CacheItem, insert, get_cacheitem, CacheItemVersion, getValue, get_if_newer.
  • To learn about the standard prerequisites required to work with all NCache client-side features, please refer to the given page on Client-Side API Prerequisites.
  • For API details, refer to: add, Cache, CacheItem, CacheItemVersion, contains, getCount, getIfNewer, insert, remove.
  • Create a new Console Application.
  • Make sure that the data being added is serializable.
  • Add NCache References by locating %NCHOME%\NCache\bin\assembly\4.0 and adding Alachisoft.NCache.Web and Alachisoft.NCache.Runtime as appropriate.
  • Include the Alachisoft.NCache.Web.Caching namespace in your application.

Retrieve and Update the Item with the Item Version

An Add operation returns the CacheItemVersion. If an item is added for the first time, a long value containing the TimeStamp of its creation is returned. This version will be incremented by "1" upon performing operations on this key in the future.

Optimistic Locking makes sure that the user always gets the most updated copy of the item from the cache. If the user keeps performing functions on the outdated version, NCache throws an exception, so that the user gets the updated item from the cache.

In the example below, a cache is used by multiple applications. The cache contains the data of products. A CacheItem is added to the cache. Both the applications fetch the item with the current version, let's say version. Application 1 modifies the productName and then reinserts the item in the cache, which updates the item version of the item to the new version. Application 2 still has the item with the version. If Application 2 updates the item's units in stock and reinserts the item in the cache, the insertion will fail. Application 2 will have to fetch the updated version to perform operation on that CacheItem.

Note

You can add an item in the cache using both Add or Insert methods.

  • The Add method adds a new item to the cache and saves the item version for the first time.
  • The Insert method adds an item in the cache, if it is not present already, whereas it overwrites the value of an existing time and updates the item version.

The following code sections explain the operations performed by the application.

  • .NET
  • Java
  • Python
  • Node.js
  • Legacy API
// Precondition: Cache is already connected
// An item is added in the cache with itemVersion
// 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
    // If result is Product type
    var prod = new Product();
    prod = cacheItem.GetValue<Product>();
    prod.UnitsInStock++;

    // Create a new cacheItem with updated value
    var updateItem = new CacheItem(prod);

    // Set the itemversion. This version will be used to compare the item version of cached item
    updateItem.Version = version;
    cache.Insert(key, updateItem);

    // If it matches, the insert will be successful, otherwise it will fail
}
else
{
    // Item could not be retrieved due to outdated CacheItemVersion
}
// Precondition: Cache is already connected
// An item is added in the cache with itemVersion
// Specify the key of the cacheItem
String key = "Product:1001";

// Initialize the cacheItemVersion
CacheItemVersion version = new CacheItemVersion();

// readThruOptions set to null
CacheItem cacheItem = cache.getCacheItem(key, version, null);

// If result is not null
if (cacheItem != null)
{
    var prod = new Product();
    prod = cacheItem.getValue(Product.Class);
    prod.unitsInStock++;

    // Update the retrieved cacheitem according to requirement
    // Create a new cacheItem with updated value
    CacheItem updatedItem = new CacheItem(prod);

    // Set the itemversion. This version will be used to compare the item version of cached item
    updatedItem.setCacheItemVersion(version);
    cache.insert(key, updatedItem);

    // If it matches, the insert will be successful, otherwise it will fail
}
# Precondition: Cache is already connected
# An item is added in the cache with ItemVersion

# Specify the key of the cacheItem
key = "Product:1001"

# Initialize the cacheItemVersion
version = ncache.CacheItemVersion(0)

cache_item = cache.get_cacheitem(key, cacheitemversion=version)

# If result is not None
if cache_item is not None:
    prod = cache_item.get_value(Product)
    prod.set_units_in_stock(20)

    # Update the retrieved cacheitem according to requirement
    # Create a new CacheItem with updated value
    updated_item = ncache.CacheItem(prod)

    # Set the item_version. This version will be used to compare the
    # item version of cached item
    updated_item.set_cache_item_version(version)

    cache.insert(key, updated_item)

    # If it matches, the insert will be successful, otherwise it will fail
// This is an async method
// Precondition: Cache is already connected
// An item is added in the cache with itemVersion
// Specify the key of the cacheItem
var key = "Product:1001";

// Initialize the cacheItemVersion
var version = new ncache.CacheItemVersion();

// readThruOptions set to null
var cacheItem = await this.cache.getCacheItem(key, null, version);

// If result is not null
if (cacheItem != null)
{
    var prod = new Product();
    prod = cacheItem.getValue(ncache.JsonDataType.Object);
    prod.unitsInStock++;

    // Update the retrieved cacheitem according to requirement
    // Create a new cacheItem with updated value
    // You also need to specify the FQN(Fully Qualified Name) of the class
    var updatedItem = new ncache.CacheItem(prod,"FQN.Product");

    // Set the itemversion. This version will be used to compare the item version of cached item
    updatedItem.setCacheItemVersion(version);

    await this.cache.insert(key, updatedItem);

    // If it matches, the insert will be successful, otherwise it will fail
}
// precondition: itemVersion is saved when item was added or inserted in cache
string key = "Product:1001";

// saved itemVersion from add or insert item call
CacheItem result = cache.GetCacheItem(key, ref itemVersion);
if (result.Value != null)
{
    if (result.Value is Product)
    {
        Product product = (Product)result.Value;
    }
}

Product product = new Product();
product.ProductID = 1001;
product.ProductName = "Chai";
product.Category = 5; // updated category

CacheItem cacheItem = new CacheItem(product);

string key = "Product:" + product.ProductID;

// saved itemVersion from add item call
cacheItem.Version = itemVersion;
CacheItemVersion newVersion = cache.Insert(key, cacheItem);

// save new version for future usage

// verify the updated version and value in cache
Note

To ensure the operation is fail-safe, it is recommended to handle any potential exceptions within your application, as explained in Handling Failures.

Retrieve the Item if a Newer Version Exists in the Cache

The GetIfNewer method can be used to fetch the existing item if a newer version is available in the cache. By specifying the current version as an argument of the method call, the cache returns the appropriate result. If the version specified is less than the one in the cache, only then does the method return a new item, else null will be returned.

The following example adds an item in the cache with the key Product:1001 and item version and then retrieves it if any newer version is available using the GetIfNewer method which fetches an item using the cache item version.

  • .NET
  • Java
  • Python
  • Node.js
// Get updated product from database against given product ID
Product product = FetchProductByProductID(1001);

// Generate a unique key for this item
string key = $"Product:{product.ProductID}";

// Create a new CacheItem
var item = new CacheItem(product);

// Add CacheItem to cache with new itemversion
CacheItemVersion version = cache.Insert(key, item);

// Get object from cache
var result = cache.GetIfNewer<Product>(key, ref version);

// Check if updated item is available
if (result != null)
{
    // An item with newer version is available
    if (result is Product)
    {
        // Perform operations according to business logic
    }
}
else
{
    // No new itemVersion is available
}
// Get updated product from database against given product ID
Product product = fetchProductByProductID(1001);

// Generate a unique key for this item
String key = "Product:" + product.getProductID();

// Create a new CacheItem
CacheItem item = new CacheItem(product);

// Add CacheItem to cache with new itemversion
CacheItemVersion version = cache.insert(key, item);

// Get object from cache
Object result = cache.getIfNewer(key,version,Object.class);

// Check if updated item is available
if (result != null)
{
    // Perform operations according to business logic
}
else
{
    // No new itemVersion is available
}
# Get updated product from database against given product ID
product = fetch_product_from_db(1001)

# Generate a unique key for this item
key = "Product:" + product.get_product_id()

# Create a new CacheItem
item = ncache.CacheItem(product)

# Add CacheItem to cache with new itemversion
version = cache.insert(key, item)

# Get object from cache
result = cache.get_if_newer(key, cacheitemversion=version, objtype=Product)

# Check if updated item is available
if result is not None:
    # Perform operations according to business logic
    print("New version is available")
else:
    # No new ItemVersion is available
    print("New version is not available")
// This is an async method
// Get updated product from database against given product ID
var product = this.fetchProductByProductID(1001);

// Generate a unique key for this item
var key = "Product:" + product.getProductID();

// Create a new CacheItem
// You also need to specifyt the FQN(Fully Qualified Name) of the class
var item = new ncache.CacheItem(product,"FQN.Product");

// Add CacheItem to cache with new itemversion
var version = await this.cache.insert(key, item);

// Get object from cache
var result = await this.cache.getIfNewer(key, ncache.JsonDataType.Object, version);

// Check if updated item is available
if (result != null)
{
    // Perform operations according to business logic
}
else
{
    // No new itemVersion is available
}

Remove Item with Item Version

An item can be removed from the cache using an overload of Remove, based on the item version. However, if the item version is different from the one in the cache, you get an exception specifying so.

The following example shows how to remove an item from the cache by specifying the Item Version using the Remove method.

Tip

You can monitor/verify removal:

  • "Cache Count" Counter in NCache Monitor or PerfMon Counters.
  • Using cache.Contains after the expiration interval has elapsed.
  • Using cache.Count before and after specifying expiration.
  • .NET
  • Java
  • Python
  • Node.js
  • Legacy API
// Get updated product from database against given product ID
Product product = FetchProductByProductID(1001);

// Cache key remains the same for this product
string key = $"Product:{product.ProductID}";

// Create a new CacheItem
var item = new CacheItem(product);

// Insert CacheItem to cache with new itemversion
CacheItemVersion version = cache.Insert(key, item);

// Remove the item from the cache using the itemVersion
cache.Remove(key, null, version);
// Get updated product from database against given product ID
Product product = fetchProductByProductID(1001);

// Cache key remains the same for this product
String key = "Product:" + product.getProductID();

// Create a new CacheItem
CacheItem item = new CacheItem(product);

// Add CacheItem to cache with new itemversion
CacheItemVersion version = cache.insert(key, item);

// Remove the item from the cache using the itemVersion
// lockHandle and writeThruOptions set to null
cache.remove(key,null,version,null,Object.class);


Object value = cache.get(key,Object.class);

// Checking if item still exists in cache or not
if(value == null)
{
    // Item has been removed from the cache
}
else
{
    // Item is still in Cache
}
# Get updated product from database against given product ID
product = fetch_product_from_db(1001)

# Cache key remains the same for this product
key = "Product:" + product.get_product_id()

# Create a new CacheItem
item = ncache.CacheItem(product)

# Add CacheItem to cache with new itemversion
version = cache.insert(key, item)

# Remove the item from the cache using the ItemVersion
cache.remove(key, version=version, objtype=Product)

value = cache.get(key, Product)

if value is None:
    # Item has been removed from cache
    print("Remove successful")
else:
    # Item is still in cache
    print("Remove failed")
// Get updated product from database against given product ID
var product = this.fetchProductByProductID(1001);

// Cache key remains the same for this product
var key = "Product:" + product.getProductID();

// Create a new CacheItem
// You also need to specify the FQN(Fully Qualified Name) of the class
var item = new ncache.CacheItem(product,"FQN.Product");

// Add CacheItem to cache with new itemversion
var version = await this.cache.insert(key, item);

// Remove the item from the cache using the itemVersion
// lockHandle and writeThruOptions set to null
await this.cache.remove(key, ncache.JsonDataType.Object, null, version, null);

var value = await this.cache.get(key, ncache.JsonDataType.Object);

if (value == null)
{
    // Item has been removed from cache
}
else
{
    // Item is still  in cache
}
// precondition: itemVersion is saved when item was added or inserted in cache
string key = "Product:1001";
// saved itemVersion from add or insert item call
object result = cache.Remove(key, itemVersion);
if (result != null)
{
    if (result is Product)
    {
        Product product = (Product)result;
    }
}

Topology Wise Behavior

  • For the Mirror and the Replicated Cache

In the Mirror Topology, when an item is added or updated, its version is generated at the Active node and the same version is then replicated to the Passive node along with the item so that when an active node becomes passive, the item version remains the same.

In the Replicated Topology, the client is connected to one node and the item version is generated at a node that receives client update/add operation, then the same item version along with the item replicates to all other nodes for data consistency.

  • For the Partitioned and the Partition-Replica Cache

In the Partitioned Topology, the item version is generated and exists on the same node that contains the item, and during state transfer, version is also transferred along with the item in case the item moves to another node.

In the Partition-Replica Topology, the version is generated on the active node, which contains the item and the same version along with the item is then replicated to its replica for data consistency and during state transfer, version is also transferred along with the item in case item moves to another node.

  • Client Cache

In the Client Cache Topology, all version-related information is maintained at the clustered cache, and whenever, a version-related API is called, the user gets its version from clustered cache.

Additional Resources

NCache provides a sample application for item locking on GitHub.

See Also

.NET: Alachisoft.NCache.Runtime.Caching namespace.
Java: com.alachisoft.ncache.runtime.caching namespace.
Python: ncache.runtime.caching class.
Node.js: Cache class.

In This Article
  • When to Use Optimistic Locking
  • Prerequisites
  • Retrieve and Update the Item with the Item Version
  • Retrieve the Item if a Newer Version Exists in the Cache
  • Remove Item with Item Version
  • Topology Wise Behavior
  • Additional Resources
  • See Also

Contact Us

PHONE

+1 (214) 764-6933   (US)

+44 20 7993 8327   (UK)

 
EMAIL

sales@alachisoft.com

support@alachisoft.com

NCache
  • NCache Enterprise
  • NCache Professional
  • Edition Comparison
  • NCache Architecture
  • Benchmarks
Download
Pricing
Try Playground

Deployments
  • Cloud (SaaS & Software)
  • On-Premises
  • Kubernetes
  • Docker
Technical Use Cases
  • ASP.NET Sessions
  • ASP.NET Core Sessions
  • Pub/Sub Messaging
  • Real-Time ASP.NET SignalR
  • Internet of Things (IoT)
  • NoSQL Database
  • Stream Processing
  • Microservices
Resources
  • Magazine Articles
  • Third-Party Articles
  • Articles
  • Videos
  • Whitepapers
  • Shows
  • Talks
  • Blogs
  • Docs
Customer Case Studies
  • Testimonials
  • Customers
Support
  • Schedule a Demo
  • Forum (Google Groups)
  • Tips
Company
  • Leadership
  • Partners
  • News
  • Events
  • Careers
Contact Us

  • EnglishChinese (Simplified)FrenchGermanItalianJapaneseKoreanPortugueseSpanish

  • Contact Us
  •  
  • Sitemap
  •  
  • Terms of Use
  •  
  • Privacy Policy
© Copyright Alachisoft 2002 - 2025. All rights reserved. NCache is a registered trademark of Diyatech Corp.
Back to top