How does ConnectionMultiplexer deal with disconnects?

23,421

Solution 1

Here is the pattern recommended by the Azure Redis Cache team:

private static Lazy<ConnectionMultiplexer> lazyConnection = new Lazy<ConnectionMultiplexer>(() => {
    return ConnectionMultiplexer.Connect("mycache.redis.cache.windows.net,abortConnect=false,ssl=true,password=...");
});

public static ConnectionMultiplexer Connection {
    get {
        return lazyConnection.Value;
    }
}

A few important points:

  • It uses Lazy<T> to handle thread-safe initialization
  • It sets "abortConnect=false", which means if the initial connect attempt fails, the ConnectionMultiplexer will silently retry in the background rather than throw an exception.
  • It does not check the IsConnected property, since ConnectionMultiplexer will automatically retry in the background if the connection is dropped.

Solution 2

Yes, you need that type of verification in order to fix broken connections. Some thread safety should also be factored in as well. This is how I usually do this:

private static ConnectionMultiplexer _redis;
private static readonly Object _multiplexerLock = new Object();

private void ConnectRedis()
{
    try
    {
        _redis = ConnectionMultiplexer.Connect("...<connection string here>...");
    }
    catch (Exception ex)
    {
        //exception handling goes here
    }
}


private ConnectionMultiplexer RedisMultiplexer
{
    get
    {
        lock (_multiplexerLock)
        {
            if (_redis == null || !_redis.IsConnected)
            {
                ConnectRedis();
            }
            return _redis;
        }
    }
}

Then I use the RedisMultiplexer property everywhere I need to call the Redis endpoint. I don't usually store the result of the GetDatabase() call because the documentation says it's a pretty lightweight call.

Share:
23,421

Related videos on Youtube

Gigi
Author by

Gigi

I run Gigi Labs and Dino's Ultima Page.

Updated on July 09, 2022

Comments

  • Gigi
    Gigi almost 2 years

    The Basic Usage documentation for StackExchange.Redis explains that the ConnectionMultiplexer is long-lived and is expected to be reused.

    But what about when the connection to the server is broken? Does ConnectionMultiplexer automatically reconnect, or is it necessary to write code as in this answer (quoting that answer):

            if (RedisConnection == null || !RedisConnection.IsConnected)
            {
                RedisConnection = ConnectionMultiplexer.Connect(...);
            }
            RedisCacheDb = RedisConnection.GetDatabase();
    

    Is the above code something good to handle recovery from disconnects, or would it actually result in multiple ConnectionMultiplexer instances? Along the same lines, how should the IsConnected property be interpreted?

    [Aside: I believe the above code is a pretty bad form of lazy initialization, particularly in multithreaded environments - see Jon Skeet's article on Singletons].

  • Mike Harder
    Mike Harder about 9 years
    A big problem with this approach is that connections will be leaked until the ConnectionMultiplexer instances are cleaned up the CLR garbage collector. If the cache is under heavy load it's possible to hit the 10k connection limit. There's also no good way to call Dispose() on the old ConnectionMultiplexer, since another thread could still be trying to use it. The best approach we have found is to ignore the IsConnected property and let ConnectionMultiplexer retry the connection on its own.
  • Gigi
    Gigi about 9 years
    @MikeHarder I had this suspicion, which is why I asked the question. Thanks for confirming and clarifying.
  • GaTechThomas
    GaTechThomas about 9 years
    Do you have a reference for the recommendation? I did find their MVC movie app example, but it would be helpful to have more background from the source. Here's what I found: azure.microsoft.com/blog/2014/06/05/…
  • GaTechThomas
    GaTechThomas about 9 years
    It seems that this would be the place to go: msdn.microsoft.com/en-us/library/dn690521.aspx
  • ranieuwe
    ranieuwe over 7 years
  • sabiland
    sabiland over 7 years
    So is this pattern proper-way ?
  • MX313
    MX313 over 3 years
    Old post but do you not need the isThreadSafe parameter set to true? as in: public Lazy(Func<T> valueFactory, bool isThreadSafe);
  • Kamran Shahid
    Kamran Shahid over 3 years
    looks like in my case it is not retrying. can someone check stackoverflow.com/questions/65261761/…
  • Doc
    Doc over 2 years
    @MX313 I know it's been a year, but in case anyone else comes this way: Lazy(Func<T>) by default assumes isThreadSafe = true and LazyThreadSafetyMode = ExecutionAndPublication. You should only need to use a constructor defining one of those if the instance initialized is not thread safe or should not allow ExecutionAndPublication. See docs.microsoft.com/en-us/dotnet/api/…