Written by Irmak Tevfik on 01 - Nov - 2014

Why to use Cache-Aside Pattern on Azure Storage?

Answer is pretty simple actually, to avoid repeatedly accessing to the Storage and also gaining impressive amount of performance on searches.
But first of all we need bear in mind that:
1- Cached data has a chance that it is might not be up to date as Data on the storage might have updated during cached time
2- Do not cache whatever you find. Analyse your needs and cache the key items.
3- Whatever you use, every cache item will have an expiration. Choose your timing carefully. If time is short, item will get wiped our and we need to re-cache it constantly.
‚Äč

Ok, for this experiment we will be using Azure Table Storage with more than 20,000 data items uploaded. For caching purposes, we will stick to Redis Cache. If we take a look at the diagram below:

We can see that we need to first hit to Redis Cache to see if we have our item available. if so, we will fetch it otherwise, pull the item from Table to re-cache. For this article, I have created a project which can be downloaded from the GitHub link at the bottom of the page. We will be focusing on the performance that we can gain by using this pattern on constant searches. 

As an example, I choose to implement a simple RegistrationId search platform where user will be searching user registrationId to get the data. Simply this relation will not likely to be updated constantly, it will be a better example for this pattern.

Created project simply loads the dummy data from methods inside "ConfigureController" setdata action. 
public async Task<ActionResult> Setdata()
{
    await SearchEngine.InitializeData();
    await SearchEngine.PutRedisData();
    return View();
}

First static method simply creates your table named as "identitytable" and generates random data for your Azure Table Storage by simply executing:
Repository.TableRepository rep = new Repository.TableRepository();
//Create table container if not exists
await rep.CreateTableAsync("identitytable");
//get records list to bulk insert
var dataList = AzureStorageTableModels.DataGenerator.GenerateIdentityTableModelData(50000);
var resultData = await rep.InsertBatchAsync<IdentityTableModel>(dataList, "identitytable");

Please be aware that you can change the parameter for GenerateIdentityTableModelData  to be what you like to be as dummy data amount. depending on your connection, this might take some time. As soon as awaited call finishes, we will run the next process to cache our keys to Redis Cache by running PutRedisData

After we have all of your dummy data ready, we can carry out our tests from Home/Index page which will simply open the view with simple Bootstrap layout. To test, first run it as non-cached(uncheck checkbox) and search for RegistrationId of your choice.

var resTime = await SearchEngine.GetByDirectSearch(model.SearchData);

Will run to get the data by filtering the Azure Table Storage directly. Once I tried I have timing shown below:



Then give it a try to run the same test for cached search:



You can see that results are almost x3 times faster!!!. Ok lets move on, once you check the codes, you will noticed that the pattern is not fully implemented as it will not re-cache the missing items. Simply change the logic in

internal async static Task<double> GetByCachedSearch(string search)
{
    Repository.TableRepository tRepo = new Repository.TableRepository();
    RedisEngine rEngine = new RedisEngine();
    var watch = Stopwatch.StartNew();
    var data = await new RedisEngine().GetCacheKeys(search);
    var timeInterval = TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalSeconds;
    return timeInterval;
}

To follow:

internal async static Task<double> GetByCachedSearch(string search)
{
    Repository.TableRepository tRepo = new Repository.TableRepository();
    RedisEngine rEngine = new RedisEngine();
    var watch = Stopwatch.StartNew();
    var data = await new RedisEngine().GetCacheKeys(search);
     
    if(data == null)
    {
        ....get data from Table
         
        ....data re-cache
         
        .... if you like, change the logic to return data not the time
    }
     
    var timeInterval = TimeSpan.FromMilliseconds(watch.ElapsedMilliseconds).TotalSeconds;
    return timeInterval;
}

Feel free to play with the project. If you find any issues, I will be glad to help.

Cache-Aside Pattern Test DOWNLOAD LINK

Enjoy Coding:)



comments powered by Disqus