Setting up a local Redis Cache for development

Azure Cache for Redis is a cache layer, built on Redis, for better performance for consuming data, particularly reads. But, like most things, I like to develop locally and unpack they way something works without having to consume cloud services. So recently I looked at how I can run Redis locally for the Discord Bot and move it into Azure when done. By adding a cache service like this inbetween the Discord Bot proxy service (a traditional .NET core app) and my microservices (using Azure Functions) I can keep the messaging between the components light weight while still accessing data at a very performant speed.

The Setup

Because I am only looking at storing entities from Discord in the cache using a simple key/value pair we could use the version of Redis Microsoft used to maintain. However, to ensure compatibility and features in case we need them, let’s use Docker for Windows to run Redis in a container. I will assume you already have Docker for Windows installed as this is relatively straight forward. Open a command prompt and use the following command to download the latest Redis image and create a new server with a development password:

docker run -p 6379:6379 --name dev-redis-server -d redis --requirepass mylocalredispassword

Once this command has completed you will have a docker container running a simple Redis server with a password. The reason we use a password locally is so that we can implement the authentication method later and using different environment variables. You can check the details for this by running the command docker ps or by opening the Docker Desktop Dashboard.

If we use the Dashboard and select the CLI option next to our running container, we will get a command line interface in our container. We can then use the redis-cli command to interact with our server. However, because we don’t have any data currently being cached there is not much to see, so the next step is to hook up the application to cache some data.

Adding a General Purpose Redis Client

Now that we have a local server running, lets configure our application to use the cache to store and read data. Redis uses the generally accepted key/value paradigms and a few, limited data sets that you can read about here. Because we are storing different objects from Discord (guilds, channels, messages etc) that have a unique ID our use case will be a pretty simple key with a JSON serialized object as the value. We are also going to use the NuGet package Stack Exchange Redis, which is a high performing Redis client for C#. I installed this by using the NuGet package manager or modifying the project file.

Next we need to initialize the Redis client using dependency injection in our project with some set configuration parameters. My Discord Bot already has this functionality plumbed up so I just needed to add the following variables in my hostsettings.json file I use for local configuration:

"Redis_Server": "localhost",
"Redis_Port": "6379",
"Redis_AccessKey": "mylocalredispassword",
"Redis_SSL": "false"

When I call the ConfigureHostConfiguration method in my Host Builder, this file has the configuration settings parsed in automatically. Later, we will set the values in production using environment variables but you could also use Azure Key Vault to be more secure. To initialize Redis I added the Redis connection as a singleton service in `ConfigureServices(IServiceCollection services) in my Startup code by adding the following:

var redisConfig = string.Format("{0}:{1},password={2},ssl={3}",
    Configuration["Redis_Server"], Configuration["Redis_Port"], Configuration["Redis_AccessKey"], Configuration["Redis_SSL"]);

This uses the configuration we specified in our hosts file to connect to our local Redis cache. If we run our code now we will be connected to our Redis server on startup, but we don’t actually do anything. So let’s go through how I inject the service into our existing code.

Adding data to the cache

To use dependency injection to access our Redis connection service I first needed to create two variables:

private readonly IConnectionMultiplexer _connectionMultiplexer;
private IDatabase cachedData;

These two variables hold the link to the Redis connection (through the IConnectionMulitplexer interface) and the usable database (through the IDatabase interface). We can then initialize the connector by adding the IConnectionMultiplexer as a initializing parameter that sets this variable. The complete initialization method with that parameter now looks like this:

public DiscordSocketService(
    ILogger<DiscordSocketService> logger,
    IConnectionMultiplexer multiplexer,
    IHostApplicationLifetime appLifetime,
    IConfiguration config)
    _logger = logger;
    _appLifetime = appLifetime;
    _config = config;
    botToken = _config["DISCORD_BOT_TOKEN"];
    eventGridDomainEndpoint = _config["EventGridDomainEndPoint"];
    eventGridDomainAccessKey = _config["eventGridDomainAccessKey"];
    _connectionMultiplexer = multiplexer;

Because the class I am using to interact with the Redis server is, in this case, a service itself, I have a startup method where I can retrieve the database link through the connection. The line of code that does that looks like this:

cachedData = _connectionMultiplexer.GetDatabase();

With the connection initialized and Redis database connected, we can use the cachedData variable to perform any Redis commands we now need. To test this I added the following command to the method that is triggered whenever the Discord bot receives a message that a guild has become available using the following code:

cachedData.StringSet("guild:" + guild.Id.ToString(), serializedGuild);

Each guild object has a unique identifier (Id) and a number of properties that I serialize into a JSON string (serializedGuild). If I run the bot, when the servers it has been added to are ready the bot will write the value to Redis. We can check this by logging into the server using the Redis CLI and running the command keys * to see if we have any entries, as well as the command get <key> to view the value.

Next Steps

Running a simple Redis server locally turned out to be a fairly quick and straight forward process. This has allowed me to build out and test some caching functionality that I have been thinking about with regards to the Discord bot. The next steps for the bot are to add a Redis server to the infrastructure build pipeline so I can push to Azure, as well as configuring Azure Functions to use the same cache.

comments powered by Disqus

Contact Me!

If you would like to contact me, please fill out the form below.