Nowadays, most of the user applications which rely on heavy database calls are incorporating distributed caching for better performance and linear scalability. To ensure optimal efficiency, users want database changes to be instantly reflected in the cache. NCache offers several techniques to keep cache data fresh and synchronized with the cache.
Firstly, NCache provides Expiration, which automatically removes data from the cache after a predefined interval. Secondly, Cache Dependency on Database ensures that cached data remains synchronized with the database, invalidating cache entries whenever the underlying data changes. Additionally, the Cache Refresher runs in the background at scheduled intervals, keeping the cache data consistently fresh and updated. This blog will explore the different strategies that NCache offers for using Cache Refresher to maintain fresh data in the cache.
Cache Refresher: Overview and Concept
Imagine you run a video streaming channel and have cached new information along with corresponding videos. Over time, these videos in the cache remain unchanged, but new videos are added, and existing ones are updated in the database. This can lead to users seeing outdated videos, creating a problem of stale data.
To address this, NCache provides the Cache Refresher feature that ensures cached data stays current by refreshing it at specific intervals. The Cache Refresher updates, adds, or removes data in the cache whenever changes occur in the data source, preventing the issue of outdated or inconsistent data.
To configure the Cache Refresher, you first need to implement the Cache Startup Loader. This pre-loads the data in the cache upon startup, in the form of datasets which represent a way to group different types of data together to achieve parallelism. Every time the cache starts, the Cache Loader automatically fetches data from the data source against the configured datasets. The Cache Refresher then keeps this pre-loaded data fresh and synchronized with the updated data in data source, by refreshing these datasets at scheduled intervals, ensuring your cache always reflects the latest data.
Different Properties of Cache Refresher
Before implementing the Cache Refresher, there are several key properties you need to understand:
- Datasets: A dataset is used to group different data types, and enables you to load or refresh them separately at different intervals or events to achieve parallelism.
- Dataset Scheduling: It is a process to refresh datasets based at different intervals, which can be set to Daily Time, Daily Interval, Weekly, or Monthly. Each dataset can have its own specific refresh schedule, determining when it will be refreshed. For instance, you might schedule a dataset for bakery products to refresh every Sunday at noon. For further details, refer to the Dataset Scheduling in Documentation.
- Refresh Interval: This is an interval after which a thread runs to check the datasets that are ready to be refreshed based on their schedule. Although, each dataset may have a different refresh schedule, but this interval remains the same at cache level, ranging from minimum of 15 minutes to a maximum of 60 minutes. You can configure the Refresh Interval through the NCache Management Center.
How to Implement Cache Refresher?
To implement the Cache Refresher, follow these steps:
- Configure the ICacheLoader interface: Define how data will be loaded into the cache by using the ICacheLoader.
- Implement LoadDatasetOnStartup: NCache will invoke the LoadDatasetOnStartup method to preload initial data into the cache from your data source.
- Implement RefreshDataset: Use the RefreshDataset method to refresh the data already loaded in the cache by the Cache Loader. This method is called periodically according to the refresh intervals set for each dataset.
Let’s suppose you have two videos in the database, one regarding bakery products and the other for clothing products. You would configure the Refresher to preload these videos into the cache. Subsequently, any updates to these datasets will be managed by refreshing them separately according to their specified intervals.
Initialize Cache and Connection
The Init method is called on Cache Startup to configure the connection. Below is a sample implementation of the Init method for the ICacheLoader interface. This method demonstrates how to open an SQL connection and initialize the cache with the specified name.
1 2 3 4 5 6 7 8 9 |
public void Init(IDictionary<string, string> parameters, string cacheName) { cache = CacheManager.GetCache(cacheName); connectionString = parameters.ContainsKey("ConnectionString") ? parameters["ConnectionString"] : null; if (connectionString != null) { connection = new SqlConnection(connectionString); } } |
Load Data on Cache Startup
In the second step, you need to implement the LoadDatasetOnStartup, a method called on cache startup to preload specified datasets into the cache from the database.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
public object LoadDatasetOnStartup(string dataset) { // Create a list of datasets to load at cache startup IList<object> datasetToLoad; switch (dataset.ToLower()) { // If dataset is products, fetch products from data source to load in cache case "products": datasetToLoad = FetchProductsFromDataSource(); // Insert fetched products in the cache foreach (var product in datasetToLoad) { string key = $"ProductID:{product.Id}"; cache.Insert(key, product); } break; // If dataset is suppliers, fetch suppliers from data source to load in cache case "suppliers": datasetToLoad = FetchSuppliersFromDataSource(); // Insert fetched suppliers in the cache foreach (var supplier in datasetToLoad) { string key = $"SupplierID:{supplier.Id}"; cache.Insert(key, supplier); } break; default: // Invalid dataset } // User context is the time at which datasets were loaded in the cache object userContext = DateTime.Now; return userContext; } |
Refresh Loaded Datasets
The RefreshDataset method, thus, invoked by the Refresher, uses context from LoadDatasetOnStartup to perform necessary actions on the cache. These actions include updating, adding, or removing data to ensure the cache remains current and accurate.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
public object RefreshDataset(string dataset, object userContext) { DateTime? lastRefreshTime; switch (dataset.ToLower()) { // If dataset is products, fetch updated products from data source case "products": lastRefreshTime = userContext as DateTime?; IList<Product> productsToRefresh = FetchUpdatedProducts(lastRefreshTime) as IList<Product>; // Insert updated products in the cache foreach (var product in productsToRefresh) { string key = $"ProductID:{product.Id}"; CacheItem cacheItem = new CacheItem(product); _cache.Insert(key, cacheItem); } break; // If dataset is supplier, fetch updated suppliers from data source case "suppliers": lastRefreshTime = userContext as DateTime?; IList<Supplier> suppliersToRefresh = FetchUpdatedSuppliers(lastRefreshTime) as IList<Supplier>; // Insert updated suppliers in the cache foreach (var supplier in suppliersToRefresh) { string key = $"SupplierID:{supplier.Id}"; CacheItem cacheItem = new CacheItem(supplier); _cache.Insert(key, cacheItem); } break; default: // Invalid dataset } // User context is the time at which datasets were refreshed userContext = DateTime.Now; return userContext; } |
Get Datasets to Refresh
Now, comes the method GetDatasetsToRefresh, which is called at every refresh interval and should only be provided if user wants to use their own custom logic to control the refresh time for different loaded datasets. This method takes the user context and assigns a RefreshPreference (chosen time to refresh the dataset) depending upon the specified dataset.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
public IDictionary<string, RefreshPreference> GetDatasetsToRefresh(IDictionary<string, object> userContexts) { DateTime? lastRefreshTime; bool datasetHasUpdated; // Create a dictionary for datasets to refresh with their Refresh Preference IDictionary<string, RefreshPreference> DatasetsToRefresh = new Dictionary<string, RefreshPreference>(); foreach (var dataset in userContexts.Keys) { switch (dataset.ToLower()) { // If dataset is products, check if dataset has been updated in data source // if yes, then refresh the dataset now case "products": lastRefreshTime = userContexts[dataset] as DateTime?; datasetHasUpdated = HasProductDatasetUpdated(dataset, lastRefreshTime); if (datasetHasUpdated) { DatasetsToRefresh.Add(dataset, RefreshPreference.RefreshNow); } break; // If dataset is suppliers, check if dataset has been updated in data source // if yes, then refresh dataset on next time of day case "suppliers": lastRefreshTime = userContexts[dataset] as DateTime?; datasetHasUpdated = HasSupplierDatasetUpdated(dataset, lastRefreshTime); if (datasetHasUpdated) { DatasetsToRefresh.Add(dataset, RefreshPreference.RefreshOnNextTimeOfDay); } break; default: // Invalid dataset } } // Return the dictionary containing datasets to refresh on polling with their refresh preferences return DatasetsToRefresh; } |
Refresh Dataset on Demand
Finally, user also has the option to refresh their pre-configured datasets at runtime through the Invoke-RefresherDataset cmdlet. Here, datasets can be refreshed immediately or within the next 24 hours using the RefreshPreference option. Here’s an example where the product dataset is refreshed immediately on demoClusteredCache on server 20.200.20.11.
1 |
Invoke-RefresherDataset -CacheName demoClusteredCache -Server 20.200.20.11 -Dataset product -RefreshPreference RefreshNow |
Configure Cache Loader and Refresher through the NCache Management Center
After implementing the Cache Startup Loader and Refresher, you can configure both through the NCache Management Center and set the refresh interval as needed. Here’s how to do it
Conclusion
In conclusion, efficient cache updates are crucial, and Cache Refresher provides a systematic approach to ensure your cache reflects database modifications accurately. With Cache Refresher, these updates are handled seamlessly and efficiently. NCache offers a range of other powerful features designed for improved performance. Explore our website to learn more about how NCache can enhance your caching strategy!