With the rapid increase in high traffic applications, scalability is the need for this era. Due to this reason, distributed caching systems have made their way into the hearts and souls of today’s applications. NCache – the leading distributed cache for .NET applications, is no exception to this.
When we generally consider a caching system, it is naturally thought of as volatile and non-persistent, that any data stored in the cache will no longer be available if the cache reboots.
Suppose an online e-commerce store with hundreds and thousands of user transactions each day, saves all of its data in a distributed cache, which results in users have to access to that data at a very rapid rate. The e-commerce store wants to save all of its data in a distributed cache that is non-volatile and to retain data even if the cache crashes or reboots due to any unforeseen circumstance.
We have a solution that uses NCache’s server-side deployments to modify your cache and make it persistent. You can find the solution on GitHub as NCache Persistent Cache. This solution uses Read-Thru and Write-Thru providers to alter the cache’s behavior as a persistent cache. A persistent cache is by no means a replacement for your regular database but merely an extension to make it faster and more accessible. It can be implemented with NET’s two leading database systems SQL and Cosmos DB without the requirement of any additional code. Let’s have a more detailed look at this.
Using NCache’s Features for a Persistent Cache
NCache persistent data store enables you to store data for longer time spans. Normally data is stored in a persistent store with a predefined expiration time or till the application sends a delete command. The persistence store sits outside the distributed cache and any data stored in the cache will automatically be saved in the persistent store to be made available when the cache gets wiped clean due to its volatile nature. On every read request made by the application, if there is no requested data in the cache then the cache will automatically retrieve the entry from the persistent store and no cache data will be lost. This is shown in Figure 1:
NCache persistent store provides you with the following server-side deployment features to ensure smooth and seamless transactions when the data stored in the cache is unavailable.
- Persistent Startup Loader: This provider loads data into the cache from the persistent store every time the cache starts up. With every write operation made in the cache, the persistent store is updated and all the persisted data is loaded into the cache each time the cache restarts. Hence there will be no loss in cached data even after the complete cache cluster goes down for any reason and then starts again.
- Persistent Write Through Provider: This provider is automatically called when you write something to the persistent cache. It replicates what is written to the persistent store so it remains available when the cache restarts There are 2 options for configuring this provider – Write-Thru and Write-Behind. These allow the user to do the replication in the background or replicate before the write operation.
With Write-Thru, on every write operation the data is simultaneously saved to the persistent store and then the control is given back to the application.
With Write-Behind, NCache en-queues the operation and does it in the background, returning the flow to the user before the database operations are performed. - Persistent Read Through Provider: This provider is called in case the user requires data that isn’t available in the cache. Cache cluster which is using Read Through provider will fetch this data from the persistent store and preserve it to the cache.
Quick Example of Persistence WriteThru Provider
Let us look at a quick example of how you can use a persistent cache with NCache. In this example, we will add an item to the cache using the WriteThru Provider. When the user adds the item to the cache, the write-through provider deployed with the cache server is called and the item is simultaneously saved inside the persistent data store. Now in case the cache shuts down or the item is removed from the cache without using Write-Thru, the item would be removed from the cache but not from the persistent store; data will not be lost as it will be inside the persistent store. When that item is required, it will be fetched from the persistent store to the cache.
Here is the basic code showing the interface of the Write-Thru provider.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
public class PersistenceWriteThruProvider : ProviderBase, IWriteThruProvider { public OperationResult WriteToDataSource(WriteOperation operation) { OperationResult operationResults = new OperationResult(operation, OperationResult.Status.Success); switch (operation.OperationType) { case WriteOperationType.Add: var items = new Dictionary<string, ProviderItemBase>(); items.Add(operation.Key, operation.ProviderItem); _persistenceProvider.Add(items); break; // Update and remove cases are similar and need to be handled } return operationResults; } } |
Here is how you can insert an item into the cache with write-thru enabled so it utilizes your deployed write-through implementation:
1 2 3 4 5 6 |
// Enable write through for the cacheItem created var writeThruOptions = new WriteThruOptions(); writeThruOptions.Mode = WriteMode.WriteThru; // Add the item in the cache with WriteThru enabled CacheItemVersion itemVersion = cache.Insert(key, cacheItem, writeThruOptions); |
NCache Details Write-Through Caching Docs
Persistence ReadThru Provider
Suppose the application wants some data from the cache. This data was previously stored in the cache but due to some reason is no longer available. Normally if the user tries to access such data, the cache would return a null value as the cache does not contain the required data and additional code containing queries would have to be written to fetch data from the database. This will cause the application logic to become too complex. The cache is first searched for the required item and if it is not available, check the database and then load the data.
When we use the NCache persistence data store, the data removed from the cache would still be stored in the persistent data store and the cache would call the ReadThruProvider implementation and the data would be called in the cache and returned to the user. So now you will get the desired data even if it has been deleted from the cache without the need for additional code.
Here is the basic code showing the interface of the Read-Thru provider:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
public class PersistenceReadThruProvider : ProviderBase, IReadThruProvider { public ProviderCacheItem LoadFromSource(string key) { return (ProviderCacheItem)(_persistenceProvider.Get(new string[] { key })[key]); } public ProviderDataTypeItem LoadDataTypeFromSource(string key, DistributedDataType dataType) { ... } public IDictionary<string, ProviderCacheItem> LoadFromSource(ICollection keys) { ... } } |
You can fetch data from the cache with read-through enabled so if it does not exist in the cache, it will fetch it from the data source as per your implementation of the read-through provider.
1 2 3 4 5 6 |
// Specify the readThruOptions for read through operations var readThruOptions = new ReadThruOptions(); readThruOptions.Mode = ReadMode.ReadThru; // Retrieve the data of the corresponding item with reads thru enabled Product data = cache.Get(key, readThruOptions); |
NCache Details Read-Through Caching Docs
Persistence Startup Loader
As discussed earlier, a cache is volatile and all data in a cache is wiped clean once the cache turns off. Upon restart, the cache is empty and needs to be repopulated. Loading each entry when it is required using the Read-Thru provider makes the process of getting data and operations slower. What if we could load all the data in the cache at cache startup? With the Cache Loader interface provided by NCache, all data from the persistent store is duplicated to the cache on startup so it is available for the application to use.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
public class PersistenceStartupLoader : ProviderBase, ICacheLoader { public LoaderResult LoadNext(object userContext) { if (persistentItems == null) persistentItems = _persistenceProvider.GetAll(); LoaderResult result = new LoaderResult(); foreach (var item in persistentItems) { result.Data.Add(new KeyValuePair<string, ProviderItemBase>(item.Key, item.Value)); } return result; } } |
NCache Details Cache Startup Loader Docs
Delete Data from Persistent Datastore
In the solution I made, the persistent store is linked to your cache so any changes made within the cache would also be made inside the persistent store. For instance, a data item is stored in cache as well as a persistent store. When the user deletes it from the cache, it will cause it to get deleted from the persistent store as well, with the help of the write-through provider. This will ensure the main cache remains in sync with the persistent data store.
However, this is completely flexible, you can alter this and have items stored in the persistent store even if the application sends in a delete command.
Why NCache?
NCache is a fast and scalable market-leading in-memory distributed cache made 100% for .NET / .NET Core applications. It provides server-side features to support cache persistence and there are a lot of configurations that can be made to use cache persistence as required. Data can be persisted from cache to any database of our choice. NCache caches application data and removes performance bottlenecks related to your data storage and databases. It enables you to store your data in the cache for longer periods of time to make sure your data is available when you need it. NCache persistence datastore eliminates the need to move data from the database to the cache every time the cache starts. This ensures the high availability of your data without the need of writing extra code!
Head over to NCache Docs to know more about how you can implement and use NCache persistent datastore along with your distributed cache.