Exclusive Locks on Items (Pessimistic Locking)
NCache provides a Pessimistic Locking mechanism that exclusively locks the cached data. This mechanism locks the item using a LockHandle, which blocks all other users from performing any write operations on that cache item. A LockHandle is associated with every locked item in the cache and is returned by the Locking API.
A locked item can be fetched/updated or unlocked only when its LockHandle is provided at the API level. However, you should do this with care to avoid data integrity issues. Pessimistic Locking is a reliable approach when the goal is to ensure data consistency.
Once a 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 specify lock timeout when 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 prevents your data from being locked for an infinite amount of time.
Forceful release of locks: Situations can arise in distributed environments where an application that acquired a lock on a cache item terminates abruptly, or completes processing on locked data without releasing it. In such cases, you need to release all locks acquired by such an application. NCache provides an unlock API to forcefully release the lock on a cache item.
Note
It is recommended to use a time-based locking mechanism to ensure that the item is automatically unlocked after a specified duration, minimizing the duration a resource is locked, and improving overall system efficiency.
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 time for an update operation, a conflict might occur, which will lead to data inconsistency.
Pessimistic Locking in this scenario will allow only one user to access the account at one time. Once the user completes the operation successfully and unlocks the item, the control is set free, allowing the second user to access the account and make changes. 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 while locked. NCache provides a method specifically for Locking, as well as, numerous overloads that manipulate the Locking mechanism.
Prerequisites
- 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: ICache, Contains, Count, Insert, Lock, LockHandle, Remove, TimeSpan, Unlock.
- 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, contains, getCount, insert, lock, LockHandle, remove, TimeSpan, unlock.
- 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,
contains,
get_count, remove, LockHandle, unlock, insert, lock, TimeSpan.
- 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, contains, getCount, insert, lock, LockHandle, remove, TimeSpan, unlock.
- Install either of the following NuGet packages in your .NET client application:
- Enterprise:
Install-Package Alachisoft.NCache.SDK -Version 4.9.1.0
- Professional:
Install-Package Alachisoft.NCache.Professional.SDK -Version 4.9.1.0
- 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.
- To learn more about the NCache Legacy API, please download the NCache 4.9 documents available as a .zip file on the Alachisoft Website.
Lock an Item Explicitly
You can explicitly lock an item before performing any operation. This method requires a TimeSpan to lock an item for a specified time. However, if you do not want the acquired lock to expire, specify TimeSpan.Zero. If no TimeSpan is provided, the item will be locked for an infinite time.
The Lock method used in this example associates a LockHandle with a key. Ensure that a single LockHandle is associated with a single key. Release the lock before reusing the handle, otherwise, it might lead to inconsistent behavior. If an item is already locked, the method returns false, but provides the updated LockHandle.
Warning
Lock an item for the minimum TimeSpan to avoid a deadlock or starvation state.
The following example creates a LockHandle, and then locks an item with the key Product:1001 for a TimeSpan of 10 seconds.This means that the item will be unlocked automatically after 10 seconds.
// Precondition: 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 timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = TimeSpan.FromSeconds(10);
// Lock the item for a timespan 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
}
// Precondition: 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 = new LockHandle();
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
// Lock the item for a timespan of 10 seconds
boolean lockAcquired = cache.lock(key, lockSpan, 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
}
# Precondtion: Cache is already connected
# Item is already added in the cache
# Specify the key of the item
key = "Product:1001"
# Create a new LockHandle
lock_handle = ncache.LockHandle()
# Specify timespan of 10 seconds for which the item remains locked
lock_span = ncache.TimeSpan(None,0,0,10)
# Lock the item for a timespan of 10 seconds
lock_acquired = cache.lock(key, lock_span, lock_handle)
# Verify if the item is locked successfully
if lock_acquired:
# Item has been successfully locked
else:
# Key does not exist
# Item is already locked with a different LockHandle
// This is an async method
// Item is already added in the cache
// Specify the key of the item
var key = "Product:1001";
// Create a new LockHandle
var lockHandle = new ncache.LockHandle();
// Specify timespan of 10 seconds for which the item remains locked
var lockSpan = new ncache.TimeSpan(None,0,0,10);
// Lock the item for a timespan of 10 seconds
var lockAcquired = await this.cache.lock(key, lockSpan, 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
}
// Using NCache Enterprise 4.9.1
// Precondition: Cache is already connected
// Specify the key of the item
string key = $"Product:1001";
// Create a new LockHandle
LockHandle lockHandle = null;
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = TimeSpan.FromSeconds(10);
// Lock the item for a timespan 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
}
Note
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 a Get Operation
An item can be locked during its retrieval from the cache. This means that the item will be inaccessible to others until you release it. In case of a key mismatch, a null value is returned.
If an item is not locked and acquireLock is set to true, you will get the item along with the LockHandle.
If you pass an incorrect or new empty LockHandle while an item is locked, or if acquireLock is set to false, a null value is returned. However, you will still get the LockHandle that was previously used to lock the item.
If an item is locked and acquireLock is set to false, but you pass the correct LockHandle which was previously used to lock the item, then you will get the value.
Warning
Lock an item for the minimum TimeSpan to avoid deadlock or thread starvation.
In this example, a key and LockHandle are specified to fetch the cached object and lock it. You need to set acquireLock to true if you want to acquire the lock. Here, the item is locked with an expiration of 10 seconds, which means it will be unlocked automatically after 10 seconds.
// Precondition: Cache is already connected
// Set acquireLock flag as true
bool acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = TimeSpan.FromSeconds(10);
// Specify the key of the item
string key = $"Product:1001";
// Create a new LockHandle
LockHandle lockHandle = null;
// Lock the item for a timespan of 10 seconds
var result = cache.Get<Product>(key, acquireLock, lockSpan, ref lockHandle);
// 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
}
// Precondition: Cache is already connected
// Set acquireLock flag as true
boolean acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
// Specify the key of the item
String key = "Product:1001";
// Create a new LockHandle
LockHandle lockHandle = new LockHandle();
// Lock the item for a timespan of 10 seconds
Object result = cache.get(key, acquireLock, lockSpan, lockHandle, Object.class);
// 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
}
# Precondition: Cache is already connected
# Specify the key of the item
key = "Product:1001"
# Set acquireLock flag as true
acquire_lock = True
# Specify timespan of 10 seconds for which the item remains locked
lock_span = ncache.TimeSpan(None,0,0,10)
# Create a new LockHandle
lock_handle = ncache.LockHandle()
# Lock the item for a timespan of 10 seconds
result = cache.get_cacheitem(key, acquirelock=acquire_lock, locktimeout=lock_span, lockhandle=lock_handle)
# Verify if the item is locked successfully
if result is not None:
# Item has been successfully locked
else:
# Key does not exist
# Item is already locked with a different LockHandle
// Precondition: Cache is already connected
// Specify the key of the item
var key = "Product:1001";
// Set acquireLock flag as true
var acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
var lockSpan = new ncache.TimeSpan(None,0,0,10);
// Create a new LockHandle
var lockHandle = new ncache.LockHandle();
// Lock the item for a timespan of 10 seconds
// CacheItemVersion set to null and Readthrough Options set to null
var result = await this.cache.getCacheItem(key,null,null, acquireLock, lockSpan, lockHandle);
// 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
}
// Using NCache Enterprise 4.9.1
// Precondition: Cache is already connected
// Specify the key of the item
string key = "Product:1001";
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
// Create a new LockHandle
LockHandle lockHandle = new LockHandle();
// Lock the item for a timespan of 10 seconds
object result = cache.Get(key, lockSpan, ref lockHandle, true);
if (result != null)
{
if (result is Product)
{
Product product = (Product)result;
}
}
Release Lock with Update Operation
While updating an item, you can release the lock, allowing others to access the cached data. To successfully release the locked item, you must specify the LockHandle that was initially used to lock the item.
The LockHandle should be the same as the one that was used to lock the item, otherwise, you will get an exception message stating "Item is Locked".
If the releaseLock is set to false, you still have to pass the correct LockHandle to update the item.
If an item is not locked, then the LockHandle and releaseLock have no effect and are ignored.
The following example locks an item in the cache, gets the item using the LockHandle, updates it, and reinserts it into the cache using the Insert API.
// Precondition: Cache is already connected
// Specify the key of the item
string key = $"Product:1001";
// Set acquireLock flag as true
bool acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
// Initialize the LockHandle
LockHandle lockHandle = null;
CacheItem item = cache.GetCacheItem(key, acquireLock, lockSpan, ref lockHandle);
var product = new Product();
product = item.GetValue<Product>();
// Update the unitsinstock for the product
product.UnitsInStock = 200;
bool releaseLock = true;
// 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, null, lockHandle, releaseLock);
// Precondition: Cache is already connected
// Specify the key of the item
String key = "Product:1001";
// Set acquireLock flag as true
boolean acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
// Initialize the lockHandle
LockHandle lockHandle = new LockHandle();
CacheItem item = cache.getCacheItem(key, acquireLock, lockSpan, lockHandle);
// Update the value of item
product = item.getValue(Product.class);
product.setUnitsInStock(200);
boolean releaseLock = true;
// 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, null, lockHandle,releaseLock);
# Precondition: Cache is already connected
# Specify the key of the item
key = "Product:1001"
# Set acquireLock flag as true
acquire_lock = True
# Specify timespan of 10 seconds for which the item remains locked
lock_span = ncache.TimeSpan(None,0,0,10)
# Create a new LockHandle
lock_handle = ncache.LockHandle()
# Lock the item for a timespan of 10 seconds
item = cache.get_cacheitem(key, acquirelock=acquire_lock, locktimeout=lock_span, lockhandle=lock_handle)
# Update the value of item
product = item.get_value(Product)
product.set_units_in_stock(20)
release_lock = True
# 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=lock_handle, releaselock=release_lock)
// Precondition: Cache is already connected
// This is an async method
// Specify the key of the item
var key = "Product:1001";
// Set acquireLock flag as true
var acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
var lockSpan = new ncache.TimeSpan(None,0,0,10);
// Initialize the LockHandle
var lockHandle = new ncache.LockHandle();
// cacheItemVersion set to null and readThruOptions set to null;
var item = await this.cache.getCacheItem(key,null,null, acquireLock, lockSpan, lockHandle);
// Update the value of item
var product = item.getValue(ncache.JsonDataType.Object);
product.unitsInStock = 200;
var releaseLock = true;
// 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
await this.cache.insert(key, item, null, lockHandle, releaseLock);
// Using NCache Enterprise 4.9.1
// Precondition: Cache is already connected
// Specify the key of the item
string key = "Product:1001";
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
// Initialize the LockHandle
LockHandle lockHandle = null;
// Lock the item for 10 seconds
cache.Lock(key, TimeSpan.FromSeconds(10), out lockHandle);
// Get the item
object itemObj = cache.Get(key);
// Modify the object
product.UnitsInStock = 200;
// 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, product);
Release Lock Explicitly
To explicitly release the lock on a previously locked cached item, you will need to specify the LockHandle that was initially used to lock the item. If the LockHandle was not saved, you can use another overload of the Unlock which only takes the key to unlock the item.
Note
If an invalid LockHandle is passed, then no exception will be thrown, but the item will remain locked.
The following example gets an item that is already locked using the LockHandle. It then unlocks the item using the Unlock API with the previously saved LockHandle.
// Precondition: Cache is already connected
// Specify the key of the item
string key = $"Product:1001";
// Set acquireLock flag as true
bool acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = TimeSpan.FromSeconds(10);
// Create a new LockHandle
LockHandle lockHandle = null;
Product result = cache.Get<Product>(key, acquireLock, lockSpan, ref lockHandle);
// Make sure that the item is already locked and the saved LockHandle is used
// Unlock locked item using saved LockHandle
cache.Unlock(key, lockHandle);
// Precondition: Cache is already connected
// Specify the key of the item
String key = "Product:1001";
// Set acquireLock flag as true
boolean acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
// Create a new LockHandle
LockHandle lockHandle = new LockHandle();
Object result = cache.get(key, acquireLock, lockSpan, lockHandle, Object.class);
// Make sure that the item is already locked and the saved LockHandle is used
// Unlock locked item using saved LockHandle
cache.unlock(key, lockHandle);
# Precondition: Cache is already connected
# Specify the key of the item
key = "Product:1001"
# Set acquireLock flag as true
acquire_lock = True
# Specify timespan of 10 seconds for which the item remains locked
lock_span = ncache.TimeSpan(None,0,0,10)
# Create a new LockHandle
lock_handle = ncache.LockHandle()
result = cache.get_cacheitem(key, acquirelock=acquire_lock, locktimeout=lock_span, lockhandle=lock_handle)
# Make sure that the item is already locked and the saved LockHandle is used
# Unlock locked item using saved LockHandle
cache.unlock(key, lock_handle)
// Precondition: Cache is already connected
// This is an async method
// Specify the key of the item
var key = "Product:1001";
// Set acquireLock flag as true
var acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
var lockSpan = new ncache.TimeSpan(None,0,0,10);
// Create a new LockHandle
var lockHandle = new ncache.LockHandle();
// cacheItemVersion set to null and readThruOptions set to null;
var result = await this.cache.getCacheItem(key,null,null,acquireLock,lockSpan,lockHandle);
// Make sure that the item is already locked and the saved LockHandle is used
// Unlock locked item using saved LockHandle
await this.cache.unlock(key, lockHandle);
// Using NCache Enterprise 4.9.1
// Precondition: Cache is already connected
// Specify the key of the item
string key = "Product:1001";
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockTimeout = TimeSpan.FromSeconds(10);
// Create a new LockHandle
LockHandle lockHandle = null;
// Lock the item explicitly
cache.Lock(key, lockTimeout, out lockHandle);
object item = cache.Get(key);
// Cast and use the item
Product product = item as Product;
// Release the lock
cache.Unlock(key, lockHandle);
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 operation that removes a key from the cache and returns the removed object to the client. If a custom object is added to the cache, the Remove method will return that object.
The LockHandle should be the same which was initially used to lock the item, otherwise, you will get the exception message stating "Item is locked".
If an item is not locked, then LockHandle is not required, and its validity is not checked.
The following example gets a previously locked item using the LockHandle, and then removes it from the cache using the saved LockHandle with the Remove API.
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.
// Precondition: Cache is already connected
// Specify the key of the item
string key = $"Product:1001";
// Initialize the LockHandle
LockHandle lockHandle = null;
// Set acquireLock flag as true
bool acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = TimeSpan.FromSeconds(10);
// Get the item using the LockHandle
Product result = cache.Get<Product>(key, acquireLock, lockSpan, ref lockHandle);
// Removing locked item using saved LockHandle
cache.Remove(key, lockHandle);
// Check if item is successfully removed
if (result != null)
{
if (result is Product)
{
Product product = (Product)result;
}
}
// Precondition: Cache is already connected
// Specify the key of the item
String key = "Product:1001";
// Create a new LockHandle
LockHandle lockHandle = new LockHandle();
// Set acquireLock flag as true
boolean acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
TimeSpan lockSpan = new TimeSpan(0, 0, 10);
Object result = cache.get(key, acquireLock, lockSpan, lockHandle, Object.class);
// Removing locked item using saved LockHandle
// cacheItemVersion and writeThruOptions set to null
cache.remove(key, lockHandle, null, null, Object.class);
Object test = cache.get(key, Object.class);
// check if item has been removed
if (test != null)
{
//Item has not been removed
}
else
{
// Item has been removed
}
# Precondition: Cache is already connected
# Specify the key of the item
key = "Product:1001"
# Set acquireLock flag as true
acquire_lock = True
# Specify timespan of 10 seconds for which the item remains locked
lock_span = ncache.TimeSpan(None,0,0,10)
# Create a new LockHandle
lock_handle = ncache.LockHandle()
result = cache.get_cacheitem(key, acquirelock=acquire_lock, locktimeout=lock_span, lockhandle=lock_handle)
# Removing locked item using saved lock_handle
cache.remove(key, Product, lockhandle=lock_handle)
test = cache.get_cacheitem(key)
# Check if item has been removed
if test is not None:
# Item has not been removed
print("Remove with lock failed")
else:
# Item has been removed
print("Remove with lock successful")
// Precondition: Cache is already connected
// This is an async method
// Specify the key of the item
var key = "Product:1001";
// Set acquireLock flag as true
var acquireLock = true;
// Specify timespan of 10 seconds for which the item remains locked
var lockSpan = new ncache.TimeSpan(None,0,0,10);
// Create a new LockHandle
var lockHandle = new ncache.LockHandle();
// CacheItemVersion and ReadThruOptions set to null
var result = await this.cache.getCacheItem(key,null,null, acquireLock, lockSpan, lockHandle, ncache.JsonDataType.Object);
// Removing locked item using saved LockHandle
// cacheItemVersion and writeThruOptions set to null
await this.cache.remove(key,ncache.JsonDataType.Object, lockHandle, null, null);
var test = await this.cache.getCacheItem(key);
// check if item has been removed
if (test != null)
{
// Item has not been removed
}
else
{
// Item has been removed
}
Special Considerations while Using API Locking
NCache provides a set of APIs both with and without a LockHandle to fetch/update the cache item. APIs that do not take a LockHandle ignore item locking. Therefore, you should use all Locking APIs for data manipulation. For example, if an item is locked and you call an update API that does not require a LockHandle as an 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 that take LockHandle as a parameter. APIs that do not take a LockHandle can still be used, but only with great care so that it does not affect data integrity.
Note
In case of Eviction/Expiration, NCache ignores locks. This means that a locked item can still be removed as a result of Expiration or Eviction.
Topology Wise Behavior
- Mirrored and Replicated Topology
In the Mirror Topology, a lock is first acquired on the active node. The same LockHandle is then replicated to the passive node. This ensures that the item remains locked even if the passive node becomes active. This behavior applies to all lock operations. Similarly, the unlock call is also replicated to the passive node to unlock the item there as well.
In the Replicated Topology, the client is connected to one node. For all lock operations, the LockHandle is generated on the node that receives the client request, and then the same LockHandle will be replicated to all other nodes for data consistency. Similarly, the unlock operation will also be replicated to all other nodes.
- Partitioned and Partition-Replica Topology
In the Partitioned Topology, the LockHandle is generated and exists on the same node that contains the item. During state transfer, the LockHandle information is also transferred along with the item, in case the item moves to another node.
In the Partition-Replica Topology, the LockHandle is generated on the active node that contains the item, and the same LockHandle is then replicated to its replica to ensure data consistency. During state transfer, the LockHandle information is also transferred along with the item, in case the item moves to another node.
In the Client Cache Topology, all lock-based operations are performed directly on the clustered cache. This means that the LockHandle is generated and stored on the clustered cache. No Locking related information is maintained in the Client 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.