Eduard Keilholz

Hi, my name is Eduard Keilholz. I'm a Microsoft developer working at 4DotNet in The Netherlands. I like to speak at conferences about all and nothing, mostly Azure (or other cloud) related topics.
LinkedIn | Twitter | Mastodon | Bsky


I received the Microsoft MVP Award for Azure

Eduard Keilholz
HexMaster's Blog
Some thoughts about software development, cloud, azure, ASP.NET Core and maybe a little bit more...

Redis Cache for Beginners

The basics of caching is that you use a key-value to store data, and then use that same key to retrieve the same data. Values remain in the cache for a (configurable) amount of time. Once your value is gone, it’s… yeah… gone… You as a developer are also able to invalidate a value in the cache. The advantage of caching stuff is that the cached data can be retrieved super fast. So if you (for example) have a relational SQL Server database, and you just materialized some data for a certain view and you know this data is going to be re-used in the near future, you’re probably want to store this data in a cache mechanism to prevent you from having to query the database again with that super fancy SQL statement with over a hundred joins.

In-memory caching

I found in-memory caching an easy way of caching. It’s available for all your ASP.NET (Core) systems, you can just add it to the services collection at startup:

services.AddMemoryCache();

You’re now good to go and use this cache mechanism by injecting IMemoryCache into your controller, service or whatever, you’re able to store values, get values and invalidate values from and to the cache. Although that sounds nice, there are a couple of problems you’re facing when you move this to the cloud.
First of all, it’s in the name, you are using memory cache. This means, that you are using the internal memory (RAM) of the hosting server. So when you’re getting excited about all this caching stuff, you are probably going to use a lot of data, meaning a lot of RAM, meaning you are about to exhaust the hosting machine.
Second is, that you are probably hosting your system in more than one instance with a load balancer on top of your instance meaning that you don’t have control over the instance handling your request and thus may cache something on instance A, and retrieve it on instance B. Because those are different machines, you will not be able to fetch your data again. You can solve this by the way by enabling the ARR Affinity cookie, but for performance reasons, you really like to have this one turned off.
So basically the problems we’re facing here, is that these kinds of caching systems don’t scale, and are not distributed.

Azure Cache for Redis

Azure Cache for Redis solves your problems. It’s available for just a little less than fourteen euros a month to play with. You may want to scale this up in production environments. You can find the Azure Cache for Redis resource in the Databases section. Or provision a Basic C0 instance using the following ARM template:

{
  "type": "Microsoft.Cache/Redis",
  "apiVersion": "2019-07-01",
  "name": "[variables('redisCacheName')]",
  "location": "West Europe",
  "properties": {
    "sku": {
      "name": "Basic",
      "family": "C",
      "capacity": 0
    },
    "enableNonSslPort": false
  }
}

This is the cheapest edition, with a max of 250Mb Cache, shared infrastructure, and a max of 256 connections, for just a little bit less than 14 euros. This is all at the time of writing this article.

Now the biggest advantage is that you now have a dedicated system for caching. When you connect to this service with a lot of instances, all these instances use the same cache storage. So data stored from instance A is instantly available on instance B. Also, when you’re getting a little bit excited, you’re easily capable of scaling your cache system up (or down when the load drops), without having to adjust your web app.

Let’s have some fun

Go ahead and find yourself a € 13.80 budget and provision an Azure Cache for Redis instance. Be sure to provision a Basic C0 instance to keep the costs low. Now create a new ASP.NET Core app and install the NuGet package that contains the cache client:

dotnet add package StackExchange.Redis

Once installed you want to connect to your cache system. I created a configuration object and a factory to create a cache connection for me. Azure Cache for Redis uses a connection string that you find on the ‘Access Keys’ blade of your freshly provisioned Azure Cache for Redis instance. The factory has an interface and an implementation that look like this:

public interface ICacheServiceFactory
{
    ConnectionMultiplexer Connection {get; }
}

public sealed class CacheServiceFactory : ICacheServiceFactory
{
    private static string _connectionString;
    private readonly Lazy<ConnectionMultiplexer> _lazyConnection =
        new Lazy<ConnectionMultiplexer>(() =>
            ConnectionMultiplexer.Connect(_connectionString));

    public CacheServiceFactory(
        IOptions<RedisCacheConfiguration> cacheConfig)
    {
        _connectionString = cacheConfig.Value.ConnectionString;
    }


    public ConnectionMultiplexer Connection => _lazyConnection.Value;
}

Now to get a connection to the cache service, you need to inject the ICacheServiceFactory into your code and get the connection property. The connection contains a method called GetDatabase(). Calling this leaves you with an IDatabase to work with.

If you’d like to store a string value, for example, use the IDatabase.StringSet() or IDatabase.StringSetAsync() method. Pass a key, a value and expiry information (if you like) and BOOM, your data is stored.

Now to fetch this data is as easy, call IDatabase.StringGet() or IDatabase.StringGetAsync(). Although I think this is not really a state-of-the-art high-end blog post with the latest features available, I still think it adds value for devs struggling with performance or getting the cache distributed. Have fun, and cache wisely! ;)