Azure Redis Cache – “No endpoints specified” Error (In dotnet core)

We use a redis server for caching some basic requests in our web apps. Recently we were seeing redis timeouts during testing… there was no server load, there were about 7 requests being processed a minute, and our redis server was reporting roughly 1% capacity for usage…. strange.

We were using the package Microsoft.Extensions.Caching.Redis Version 2.0.0 and had no problems with it before, but now under basic load, we were getting timeout errors.

A bit of investigation shows that the package hadn’t been updated in 2 years, and actually the updated version is Microsoft.Extensions.Caching.StackExchangeRedis Version 3.1.1 which was updated 11 hours ago at the time of writing this post. There were no links on the original nuget page to indicate that it had moved, you just had to figure it out.

download
Hooray it was a simple fix all along. </lies>

Great! Maybe I’ve just been using some outdated libraries, and this will work better with our Azure Redis server, which was deployed at version 3.x.x but is now automatically updated to 4.x.x behind the scenes.

In theory I should be able to use my same config while only slightly tweaking my DI setup.

services.AddDistributedRedisCache(option =>
{
    option.Configuration = String.Format(
        Configuration.GetConnectionString("RedisCache"),
        Configuration.GetValue("RedisPassword"));
    option.InstanceName = Configuration.GetValue("RedisCacheInstance");
});

becomes:

services.AddStackExchangeRedisCache(option =>
{
    option.Configuration = String.Format(
        Configuration.GetConnectionString("RedisCache"),
        Configuration.GetValue("RedisPassword"));
    option.InstanceName = Configuration.GetValue("RedisCacheInstance");
});
And we should be good to go right? … Wrong.
When trying to use this I get a big exception which tells me “No Endpoints Specified” which is weird, because the connection string is correct and it, by definition, specifies the endpoint to connect to.
I was even following this great tutorial by Tobias Zimmergren to make sure I wasn’t doing something stupid. But it seems at some point there’s been a bug/feature introduced that means you can’t specify the connection string, and then add some other options. It’s now a “one or the other” setup… but there’s no explanation of that’s what the problem is, or even a hint as to that’s what’s wrong.
I was able to find the error here in the StackExchange.Redis source code and it seems that the configuration is either one or the other. You can’t mix and match any more.
internal static ConfigurationOptions PrepareConfig(object configuration)
{
    if (configuration == null) throw new ArgumentNullException(nameof(configuration));
    ConfigurationOptions config;
    if (configuration is string s)
    {
        config = ConfigurationOptions.Parse(s);
    }
    else if (configuration is ConfigurationOptions configurationOptions)
    {
        config = (configurationOptions).Clone();
    }
    else
    {
        throw new ArgumentException("Invalid configuration object", nameof(configuration));
    }
    if (config.EndPoints.Count == 0) throw new ArgumentException("No endpoints specified", nameof(configuration));
    config.SetDefaultPorts();
    return config;
}
So now, to get the redis cache working you need to break your connection string apart and specify your options manually, and get rid of your connection string.
Now my setup in startup.cs looks like this:
services.AddStackExchangeRedisCache(option =>
{
    option.InstanceName = Configuration.GetValue<string>("RedisCacheInstance");
    option.ConfigurationOptions = newConfigurationOptions()
    {
        EndPoints = { Configuration.GetValue<string>("RedisServer"), Configuration.GetValue<string>("RedisPort") },
        Password = Configuration.GetValue<string>("RedisPassword"),
        ConnectRetry = 5,
        ReconnectRetryPolicy = newLinearRetry(1500),
        Ssl = true,
        AbortOnConnectFail = false,
        ConnectTimeout = 5000,
        SyncTimeout = 5000,
        DefaultDatabase = 0
     };
});
And everything seems to be working fine (I’ve added some retry options in there as well now.
So in part… when upgrading your Redis IDistributed Cache in dotnet core…. you can’t mix connection strings and connection options. You have to use one or the other.
Hopefully this post will help someone else find a solution quicker than I did, as I couldn’t find an explanation anywhere.
Peace.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

Blog at WordPress.com.

Up ↑

%d bloggers like this: