TRUNGTQ

Think Big, Act Small, Fail Fast and Learn Rapidly

NAVIGATION - SEARCH

Response Caching and In Memory Caching in ASP.NET Core 1.0

Response Caching and In Memory Caching in ASP.NET Core 1.0

Introduction

ASP.NET 5 supports a couple of caching mechanisms to improve the performance and responsiveness of applications. Here are few options to implement cache at various levels.

  • Response Cache
  • In Memory Cache
  • Distributed Caching in SQL Server or In memory cache server

Currently ASP.NET 5 does not support Output Caching but it will be added in a future version. In this article we will explore Response Cache and In Memory Cache.

Response Cache

Response Caching adds cache related header in response from application if we decorate ResponseCache attribute to Action, Controller or Global level. ResponseCache does not store HTTP response in server unlike Output Cache in prior versions of ASP.NET. It just adds the "Cache-Control" header in response, now it's the client's choice to cache the response in the browser or some intermediate server, if any. It can reduce the number of server roundtrip if the browser or cache server cached the previous response and server to any subsequent request. In this example, we have decorated ResponseCache Action with the "ResponseCache" attribute which takes the Duration parameter.

[ResponseCache(Duration =60)]
public ActionResult ResponseCache()
{
    return View("Index");
}
 

And in response we are seeing that the Cache-Control header has added in HTTP response

The Value of the Cache-Control header could be Public, Private or no-cache. Let’s discuss each one of them.

Public

Indicates that the response is allowed to cache by any caching mechanism. Mostly if the page is independent of user then we can make it publicly cacheable.

Private

The value of Cache-Control header is "privte," which means it will not be cached by any intermediate cache server. We can specify "Location = ResponseCacheLocation.Client" along with ResponseCache to make it private.

[ResponseCache(Duration =60 , Location = ResponseCacheLocation.Client)]
public ActionResult ResponseCache()
{

                return View("Index");
}
 

In response, we are seeing that the value of the "Cache-Control" header is private. So the response is allowed to cache by only the user’s private area, like the Browser.

No Cache

If we don’t want to cache a response in any cache layer, we can specify the Location value as none. Here is example code.

[ResponseCache(Duration =60 , Location = ResponseCacheLocation.None)]
public ActionResult ResponseCache()
{
    return View("Index");
}
 

Additional HTTP headers used for caching include "Pragma" and "Vary", which are described below. Learn more about Caching in HTTP from the specification.

Attributes of ResponseCache

The ResponseCache attribute supports many parameters, in this section we will understand each one of them.
Duration: Type int
The maximum time (in seconds) the response should be cached. Required unless NoStore is true.
Location: Type ResponseCacheLocation
The location where the response may be cached. May be Any, None, or Client. Default isAny. Based on this value, the value of the "Response-Cache" header changes.
NoStore :Type bool
Determines whether the value should be stored or not, and overrides other property values. When true, Duration is ignored and Location is ignored for values other than None. The common scenario to use No Store is may be to display error page. Most likely error page is user specific and we don’t want to show one error page generated by one user to another user.
VaryByHeader : Type string
When set, a vary response header will be written with the response. When the value of the header will change, the cached version will be invalid.
CacheProfileName : Type string
When set, determines the name of the cache profile to use. We can specify cache profile in Configuration section of ASP.NET 5 application.
Order int
The order of the filter (from IOrderedFilter).

Cache Profile

We can maintain the Cache profile in application level and use in action or controller level. The mechanism will reduce code repetation. In this example we have created one cache profile called private cache.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc(options =>
    {
        options.CacheProfiles.Add("PrivateCache",
            new CacheProfile()
            {
                Duration = 60,
                Location = ResponseCacheLocation.None
            });

    });
    services.AddCaching();
}
 

Now, we can use the profile in controller/action level. Here is example code to implement same.

[ResponseCache(CacheProfileName = "PrivateCache")]

public ActionResult ResponseCache()
{
    return View("Index");
}
 

In memory cache

In memory caching uses memory allocated to process for storing information. It is not distributed and scalable because the cached data is stored in server process. To configure In Memory cache, we need to add following packages in project.json file.

"Microsoft.Extensions.Caching.Memory": "1.0.0-rc1-final",
"Microsoft.Extensions.Caching.Abstractions": "1.0.0-rc1-final"

Caching in ASP.NET Core is a service that should be referenced from your application by Dependency Injection. To register the caching service and make it available within your app, add the following line to your ConfigureServices method in Startup.cs file. Here is example code.

public void ConfigureServices(IServiceCollection services)
{
    services.AddMvc();
    services.AddCaching();
}
 

Get and Set value to In Memory Cache

In this example, we will learn to get and set value to In Memory cache. To get value we can use either the Get or TryGateValue() Function. To set the value we can use the Set() function. Here is full code example of In Memory Cache.

public class HomeController : Controller
{
    private IMemoryCache cache;
    private static int data;
    public HomeController(IMemoryCache cache)
    {
        this.cache = cache;
    }
    public ActionResult Index()
    {
            var Time =  DateTime.Now.ToLocalTime().ToString() ;
            this.cache.Set("Time", Time, new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = (DateTime.Now.AddMinutes(1) - DateTime.Now  ) });
            ViewBag.Time = Time;
            return View();
    }
    public IActionResult GetCache()
    {
        string Time = null;
        if (this.cache.TryGetValue("Time", out Time))
        {
            ViewBag.Time = Time;
        }
        else
            ViewBag.Time = DateTime.Now.ToLocalTime().ToString();
        return View("Index");
    }
 

The Index action is catching current date time in cache and when we are calling the GetCache() function, it checks in cache at first. If cache is present then will return from cache otherwise it will return fresh date time. The duration of Cache is one minute because in time of cache set we have specified the value.

this.cache.Set("Time", Time, new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = (DateTime.Now.AddMinutes(1) - DateTime.Now  ) });

in AbsoluteExpirationRelativeToNow property. The other way to set the expire value as shown below.
_this.cache.Set("key", "Value", new MemoryCacheEntryOptions()         .SetAbsoluteExpiration(TimeSpan.FromMinutes(1)));

Setting cache priority

By default, an instance of MemoryCache will automatically manage the items stored, removing entries when necessary in response to memory pressure in the app. You can influence the way cache entries are managed by setting their CacheItemPriority when adding the item to the cache. For example, if you have an item you want to keep in the cache unless you explicitly remove it, you can use the NeverRemove priority option.

 new MemoryCacheEntryOptions()
.SetPriority(CacheItemPriority.NeverRemove))
 

If you want to explicitly remove cache entry, you can call remove() function like this. cache.Remove("Key");

Conclusion

Response cache allows the client to cache response various cache levels and in Memory cache allows to cache in process memory. However, they are not distributed and scalable in nature. ASP.NET 5 supports Distributed cache as well where we can store cache data in SQL or Redis clustered.

LINK: https://www.codeproject.com/Articles/1111260/Response-Caching-and-In-Memory-Caching-in-ASP-NET

Distributed cache using Redis and ASP.NET Core

What is Redis ?

Redis is a super fast non-relational database that uses keys to map to different data types. It is a key value data store (NoSQL) allowing to solve efficiently many different problem sets. Redis was created by Salvatore Sanfilippo in 2009, and Sanfilippo still remains the lead developer of the project today. It is a mature and hugely popular open source project, being used by many companies and in countless mission-critical production environments.

Here is an interview with the inventor of Redis, Salvatore Sanfilippo.

Why is Redis popular?

Not only is it extremely effective, but it is also relatively simple. Getting started with Redis is quite fast, and it usually takes only a few minutes to set up and get them working within an application. Thus, a small investment of time and effort can have an immediate, dramatic impact on performance of the application.

Just to name two cases when Redis is helpful:

  • Redis is used at Pinterest – see use case
  • or at Twitter, where Raffi Kirkorian, VP of Engineering at Twitter, explains how Redis helps to support over 30 billion timeline updates per day based on 5000 tweets per second or 400,000,000 tweets per day – see here the presentation.

Easy access the code

The full client web application that display the Twitter messages could be found on Github – https://github.com/fpetru/redis-aspnetcore-caching.

To install

Here are all the things needed to be installed locally:

Installing Redis Server on Windows

Create a new ASP.NET Core project, and from Nuget select for installing Redis-64. The server is installed in the default Nuget path. To start the server run next in command prompt:

C:\Users\[logged in user]\.nuget\packages\Redis-64\[version installed]\tools>redis-server.exe
# in my case
C:\Users\petru\.nuget\packages\Redis-64\3.0.503\tools>redis-server.exe

redis-server

In the same folder, there is a document describing how to install Redis as a Windows Service (check the file Windows Service Documentation.docx). For running the simple scenarios from this article, running just from command line should be enough.

Caching in ASP.NET Core using Redis

To use Redis in ASP.NET Core, we need to reference Microsoft.Extensions.Caching.Redis.Core package. Additionally, in our sample we would need also the Session package. Installing these could be done either using Nuget or extending the project.json file:

"Microsoft.Extensions.Caching.Redis.Core": "1.0.3",
"Microsoft.AspNetCore.Session": "1.1.0"

To enable Redis in the application we need to add AddDistributedCache method in ConfigureServices method.

public void ConfigureServices(IServiceCollection services)
{
    services.AddDistributedRedisCache(options =>
    {
        options.InstanceName = "Sample";
        options.Configuration = "localhost";
    });
    services.AddMvc();
}
 

To enable the session, we need to do changes in both ConfigureServices and Configure:

public void ConfigureServices(IServiceCollection services)
{
    services.AddDistributedRedisCache(options =>
    {
        options.InstanceName = "Sample";
        options.Configuration = "localhost";
    });

    services.AddSession();
    services.AddMvc();
}

public void Configure(IApplicationBuilder app, 
    IHostingEnvironment env, ILoggerFactory loggerFactory)
{
    app.UseSession();
    app.UseMvc(routes =>
    {
        routes.MapRoute(
            name: "default",
            template: "{controller=Home}/{action=Index}/{id?}");
    });
}
 

How do we access Redis?

To cache a value in Redis we use:

var valueToStoreInRedis = Encoding.UTF8.GetBytes("This is a cached value from Redis");
HttpContext.Session.Set("TestProperty", valueToStoreInRedis);

To retrieve the value from cache we use:

var valueFromRedis = default(byte[]);
if (HttpContext.Session.TryGetValue("TestProperty", out valueFromRedis))
    valueToDisplay = Encoding.UTF8.GetString(valueFromRedis);

Twitter client example

External interfaces to connect to social platforms are usually relatively slow to access. If we would do a web application showing messages from Twitter, it would take a couple of seconds to load the page (to make the search and retrieve). If the same user would connect again to our application, Redis could retrieve from memory the same messages, without accessing the server again. Caching the results, and updating them only when new messages appear, bring a huge improvement to the overall performance.

To keep the things simple, I have earlier written a small client in Python, that connects to Twitter and save the details in a JSON file. You can read more details in the article Visual Studio Code – Connect to Twitter with Python. It includes the full source code, and very fast you can access and save data in a JSON file.

In our sample here, we start from an already available JSON file.

[
 {
        "Id": 0,
        "ProfileImage": "https://pbs.twimg.com/profile_images/1772973596/inogiclogo_normal.png",
        "ProfileDescription": "Microsoft Partner with Gold Competency in #Dynamics365 #MSDynCRM providing Solns/Services. Innovators of @Maplytics & Inolink #QuickBooks Int #MSDyn365 #MVPBuzz",
        "Username": "Inogic",
        "Text": "Execute the Global Action Using Web API in Dynamics CRM https://t.co/DAuzP6L7FE #MSDyn365 #webapi https://t.co/v0XgyotaFn",
        "ScreenName": "@inogic"
    },
    ....
]

Retrieval of data from Twitter takes a couple of seconds. To simulate this, we add a delay of 20 seconds in our code. Here is the code from the controller.

public IActionResult Index()
{
	var watch = Stopwatch.StartNew();
	string jSONText = RetrieveOrUpdateRedis();
	watch.Stop();

	TempData["DataLoadTime"] = watch.ElapsedMilliseconds;
	var itemsFromjSON = JsonConvert.DeserializeObject<ienumerable>(jSONText);
	return View(itemsFromjSON);
}

private string RetrieveOrUpdateRedis()
{
	var valueFromRedis = default(byte[]);
	string valueToReturn = string.Empty;
	if (HttpContext.Session.TryGetValue("TwitterDataset", out valueFromRedis))
	{
		// Retrieve from Redis
		valueToReturn = Encoding.UTF8.GetString(valueFromRedis);
		TempData["DataLoadType"] = "From Redis";
	}
	else
	{
		// read the file and update the URLs
		var jSONText = System.IO.File.ReadAllText("twitter.json");
		valueToReturn = GetUpdatedFileContent(jSONText);

		// add an artificial delay of 20 seconds (simulating the search is done directly against a Twitter server)
		Thread.Sleep(20000);

		// store values in Redis
		var valueToStoreInRedis = Encoding.UTF8.GetBytes(valueToReturn);
		HttpContext.Session.Set("TwitterDataset", valueToStoreInRedis);
		TempData["DataLoadType"] = "From file";
	}

	return valueToReturn;
}

// a minimum data processing, updating the URLs
private string GetUpdatedFileContent(string jSONText)
{   
	var itemsFromjSON = JsonConvert.DeserializeObject<ienumerable>(jSONText);
	foreach (var item in itemsFromjSON)
	{
		Regex r = new Regex(@"(https?://[^\s]+)");
		item.Text = r.Replace(item.Text, "$1");
	}

	return JsonConvert.SerializeObject(itemsFromjSON);
}
And here is the code to display data in the view. Twitter messages are displayed within a table:

@{
    ViewData["Title"] = "Home Page";
}

@model IEnumerable


Twitter messages #webapi

Time to load the data: @TempData["DataLoadTime"]ms

From where is loaded: @TempData["DataLoadType"]


        @foreach (var item in Model)
        {
             
        }
#MessageWho
@item.Id@Html.Raw(item.Text)
@item.Username
@item.ScreenName

How fast does the application run with Redis?

The initial time to load is displayed below (this includes also the artificial timeout of 20 seconds):

Just taking the results from Redis runs much faster. This exclude any “connection” to Twitter or any other internal update. Here is the print screen with the data retrieved from memory:

To replicate this multiple times, and not just at the beginning, use command flushall to clean entire Redis cache:

C:\Users\[logged in user]\.nuget\packages\Redis-64\[version installed]\tools>redis-cli.exe

# in my case
C:\Users\petru\.nuget\packages\Redis-64\3.0.503\tools>redis-cli.exe

# and then
127.0.0.1:6379> flushall

Is Redis just a cache? When to use it?

Redis is much more than just a cache. Like any cache, Redis stores indeed [key, value] pairs. More than this, Redis allows us to use values in a very efficient ways. Using different data structures (not just strings), we gain a lot of power (such as the ability to fine-tune cache contents and durability) and greater efficiency overall. Once we start using the data structures, the efficiency boost becomes tremendous for specific application scenarios.

Here is a summary of the data structures, along with few concrete use cases associated with each type:

 Uses
StringsCaches, counters, and clever bit-operations.

 

Session management is key to many
online application. Their success depends by responsiveness and accuracy of data
displayed, just to name here shopping carts for eCommerce websites. Distributed cache,
it is a great alternative to the local cache.

ListsQueues, stacks and cyclical lists. All these help to decouple processes, while receiving
many new requests.

 

A simple example would be an web application that is used to place
orders. The processing of the orders may take a while, and these
could be decoupled using queues.

HashesThese are field-value pairs, very efficient when storing many small values.

 

An example would be for implementing of a black lists checking feature.
Or accessing very fast lots of images from a gallery.

SetsAre very good for expressing relations between objects.

 

These could make easy implementation of social tags in social networking
applications, or discussion tags in blogs etc.

Sorted SetsThese are a mix between sets and Hashes. One example would be online games, where
scoreboards and player statistics are vital.
HyperLogLogCounting unique things. A simple use case, counting unique queries
performed by users in a search form during the day.

 

LINK: http://www.qappdesign.com/distributed-cache-using-redis-and-aspnet-core/