Request Metrics v3.0

Are you ready for INP?

Search update happens on March 12th. Is your INP fast?

Episode 11: Connecting to Redis in .NET Core 3.1

We’ve finally finished screwing around with basic groundwork. Today we start writing actually useful application code! First, can we connect to Redis? Now we get to start writing “real” application code! Redis is an important piece of the Request Metrics puzzle. We should start using it sooner rather than later. Connecting to Redis implies some connection strings, so we’ll create a basic environment based configuration while we’re thinking about it.

Connecting To Redis From .NET Core

Redis clients are relatively complex compared to other service clients. They are expected to handle a number of cases that the server might handle in other data stores. This complexity means we’ll use a third party package instead of rolling our own.

StackExchange.Redis is the de facto official .NET Redis client and works with both .NET Core and “classic” .NET. After the package is added through Nuget, we write the minimum code necessary to prove our connection is working and Redis is up:


using StackExchange.Redis;

public class Redis {

  // Return Redis server information to prove we can connect to our Redis instance
  public static string TestConnection() {
    var connection = ConnectionMultiplexer.Connect("localhost:6379");

    // Server is frequently where more "admin" type operations are
    var server = connection.GetServer(connection.GetEndPoints().First());
    var serverInfo = server.InfoRaw();

    /* serverInfo should contain a string like:
        # Server
        redis_version:4.0.9
        redis_git_sha1:00000000
        redis_git_dirty:0
        redis_build_id:9435c3c2879311f3
        redis_mode:standalone
        ...
    */

    return serverInfo;
  }

}
BasicRedisTest.cs

Super Basic, Environment Based Configuration File

The Redis address changes between environments which means the configuration should change for each environment. We could use appsettings.json (The .NET Core replacement for app.config and web.config), but it is a pain to deal with.

Instead we use a standalone, JSON configuration file which is loaded based on an environment variable. Our configuration doesn’t have much (we only have Redis so far!):


{
  "Local": {
    "Environment": "Local",
    "RedisConnectionString": "localhost:6380,allowAdmin=true",
  },
  "Dev": {
    "Environment": "Dev",
    "RedisConnectionString": "localhost:6379,allowAdmin=true",
  },
  "Prod": {
    "Environment": "Prod",
    "RedisConnectionString": "prod-redis-server:6379,allowAdmin=true",
  }
}
myapp.config.json

NOTE: Don’t forget to set the config file’s “Copy to Output Directory” property within Visual Studio!

The configuration file is loaded by a simple class:


public class MyConfig
{
    public const string ENVIRONMENT_VARIABLE_NAME = "MY_APP_ENV";

    // Values defined in the json file for each environment
    public MyEnvironment Environment { get; set; }
    public string RedisConnectionString { get; set; }

    // The app configuration for the current environment
    public static MyConfig Current { get; set; }

    public static void LoadFromEnvironmentVariable()
    {
        var environment = System.Environment.GetEnvironmentVariable(ENVIRONMENT_VARIABLE_NAME);
        Enum.TryParse(typeof(MyEnvironment), environment, out var env);
        if (string.IsNullOrEmpty(environment) || env == null)
        {
            throw new Exception($"Hey, you need to set the App Environment Variable!");
        }

        Load((MyEnvironment)env);
    }

    public static void Load(MyEnvironment env)
    {
        var path = Assembly.GetExecutingAssembly().Location;
        var dir = Path.GetDirectoryName(path);
        var json = File.ReadAllText(Path.Combine(dir, "myapp.config.json"));
        var config = JsonSerializer.Deserialize<Dictionary<string, MyConfig>>(json);
        Current = config[env.ToString()];
    }
}

// Because we love enums
public enum MyEnvironment
{
    Unknown, // Beware the un-initialized enum!
    Local,
    Dev,
    Prod
}
MyConfig.cs

The above code is a little gross, but you’ll never need to look at it again. The payoff is that fetching a configuration value is easy:


// This just needs to be called once on application startup
EnvironmentConfig.LoadFromEnvironmentVariable();

// Fetch config wherever you need it!
var redisConnectionString = EnvironmentConfig.Current.RedisConnectionString;

Now we’ve proven that our .NET Core app can connect to Redis. Our “not invented here” tendencies were soothed by a super basic environment configuration.

Next, we’ll set the environment variable on our dev server using Ansible to tell our application which environment it’s in. Also, we’ve neglected testing up till now and we need to find out how unit testing is done in .NET Core.