Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Redis Timeout Expired message on GetClient call

I hate the questions that have "Not Enough Info". So I will try to give detailed information. And in this case it is code.

Server: 64 bit of https://github.com/MSOpenTech/redis/tree/2.6/bin/release

There are three classes:

DbOperationContext.cs: https://gist.github.com/glikoz/7119628

PerRequestLifeTimeManager.cs: https://gist.github.com/glikoz/7119699

RedisRepository.cs https://gist.github.com/glikoz/7119769

We are using Redis with Unity ..

In this case we are getting this strange message:

"Redis Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use.";

We checked these:

  • Is the problem configuration issue

  • Are we using wrong RedisServer.exe

  • Is there any architectural problem

Any idea? Any similar story?

Thanks.

Extra Info 1

There is no rejected connection issue on server stats (I've checked it via redis-cli.exe info command)

like image 502
Oguz Karadenizli Avatar asked Oct 23 '13 14:10

Oguz Karadenizli


People also ask

How do I set timeout in Redis?

To create a Redis with an expiration time, use the SET command and the EX option to set the expiration time. The EX option takes a number in seconds and sets the number of seconds the key is valid until expiration. You can also use PX to specify the expiration time in Milliseconds.

What is Redis timeout?

Redis client uses a single TCP connection and can only read one response at a time. Even when a first operation times out, it does not stop the data being sent to/from the server. Because of this, it blocks other requests and causes timeouts.

How do I close Redis client connection?

Due to the single-threaded nature of Redis, it is not possible to kill a client connection while it is executing a command. From the client point of view, the connection can never be closed in the middle of the execution of a command.

What is client connection in Redis?

Redis accepts clients connections on the configured TCP port and on the Unix socket if enabled. When a new client connection is accepted the following operations are performed: The client socket is put in the non-blocking state since Redis uses multiplexing and non-blocking I/O.


1 Answers

I have continued to debug this problem, and have fixed numerous things on my platform to avoid this exception. Here is what I have done to solve the issue:

Executive summary:

People encountering this exception should check:

  1. That the PooledRedisClientsManager (IRedisClientsManager) is registed in a singleton scope
  2. That the RedisMqServer (IMessageService) is registered in a singleton scope
  3. That any utilized RedisClient returned from either of the above is properly disposed of, to ensure that the pooled clients are not left stale.

The solution to my problem:

First of all, this exception is thrown by the PooledRedisClient because it has no more pooled connections available.

I'm registering all the required Redis stuff in the StructureMap IoC container (not unity as in the author's case). Thanks to this post I was reminded that the PooledRedisClientManager should be a singleton - I also decided to register the RedisMqServer as a singleton:

 ObjectFactory.Configure(x =>
 {
     // register the message queue stuff as Singletons in this AppDomain
     x.For<IRedisClientsManager>()
         .Singleton()
         .Use(BuildRedisClientsManager);
     x.For<IMessageService>()
         .Singleton()
         .Use<RedisMqServer>()
         .Ctor<IRedisClientsManager>().Is(i => i.GetInstance<IRedisClientsManager>())
         .Ctor<int>("retryCount").Is(2)
         .Ctor<TimeSpan?>().Is(TimeSpan.FromSeconds(5));

     // Retrieve a new message factory from the singleton IMessageService 
     x.For<IMessageFactory>()
         .Use(i => i.GetInstance<IMessageService>().MessageFactory);
 });

My "BuildRedisClientManager" function looks like this:

private static IRedisClientsManager BuildRedisClientsManager()
{
    var appSettings = new AppSettings();
    var redisClients = appSettings.Get("redis-servers", "redis.local:6379").Split(',');
    var redisFactory = new PooledRedisClientManager(redisClients);
    redisFactory.ConnectTimeout = 5;
    redisFactory.IdleTimeOutSecs = 30;
    redisFactory.PoolTimeout = 3;
    return redisFactory;
 }

Then, when it comes to producing messages it's very important that the utilized RedisClient is properly disposed of, otherwise we run into the dreaded "Timeout Expired" (thanks to this post). I have the following helper code to send a message to the queue:

public static void PublishMessage<T>(T msg)
{
    try
    {
       using (var producer = GetMessageProducer())
       {
           producer.Publish<T>(msg);
       }
    }
    catch (Exception ex)
    {
        // TODO: Log or whatever... I'm not throwing to avoid showing users that we have a broken MQ
    }
}

private static IMessageQueueClient GetMessageProducer()
{
    var producer = ObjectFactory.GetInstance<IMessageService>() as RedisMqServer;
    var client = producer.CreateMessageQueueClient();

    return client;
}

I hope this helps solve your issue too.

like image 194
nover Avatar answered Sep 30 '22 17:09

nover