Search code examples
c#booksleeve

Booksleeve closing connection prematurely?


I am having difficulty with the 1.1.0.5 version of Booksleeve in VS2010 working the way I intend to use it. What's happening is after perform and wait for an operation, Booksleeve sometimes leaves the connection in a closed state, so operations down stream throw exceptions.

The simplest issue I am having is this:

static void Main(string[] args)
{
     Func<RedisConnection> getNewRedisConnection = () =>
        {
            RedisConnection conn = new RedisConnection("Belasco");
            conn.Error += (obj, eArgs) => { throw eArgs.Exception; };
            return conn;
        };

    RedisConnection redisConn = null;
    TaskScheduler.UnobservedTaskException += new EventHandler<UnobservedTaskExceptionEventArgs>(TaskScheduler_UnobservedTaskException);

    redisConn = getNewRedisConnection();
    redisConn.Open();

    var test1 = redisConn.Sets.GetAllString(0, "test1");
    var testValues1 = test1.Result;
    //var testValues1 = redisConn.Wait(test1);

    var test2 = redisConn.Sets.GetAllString(0, "test2");
    var testValues2 = test2.Result;
    //var testValues2 = redisConn.Wait(test2);

    redisConn.Close(false);

    Console.WriteLine("Done");
    Console.ReadKey();
}

static void TaskScheduler_UnobservedTaskException(object sender, UnobservedTaskExceptionEventArgs e)
{
    throw new NotImplementedException();
}

Line var test2 = redisConn.Sets.GetAllString(0, "test2"); throws 'The queue is closed' exception.

Now if I close and re-open the connection inbetween the requests, things seemingly work.

    var test1 = redisConn.Sets.GetAllString(0, "test1");
    var testValues1 = test1.Result;
    //var testValues1 = redisConn.Wait(test1);

    redisConn.Close(false);
    redisConn = getNewRedisConnection();
    redisConn.Open();

    var test2 = redisConn.Sets.GetAllString(0, "test2");
    var testValues2 = test2.Result;
    //var testValues2 = redisConn.Wait(test2);

However if I perform a "setting" operation, all seems to go back to broken.

    redisConn = getNewRedisConnection();
    redisConn.Open();

    var test0 = redisConn.Sets.Add(0, "test1", new string[] { "11", "22", "33", "44", "55" });
    redisConn.Wait(test0);

    redisConn.Close(false);
    redisConn = getNewRedisConnection();
    redisConn.Open();

    var test1 = redisConn.Sets.GetAllString(0, "test1");
    var testValues1 = test1.Result;
    //var testValues1 = redisConn.Wait(test1);

    redisConn.Close(false);
    redisConn = getNewRedisConnection();
    redisConn.Open();

    var test2 = redisConn.Sets.GetAllString(0, "test2");
    var testValues2 = test2.Result;
    //var testValues2 = redisConn.Wait(test2);

I also have the same issues with Transactions, but I feel it's stemming from the same issue. The reason why I need to retrieve values, then queue up another command is because my later actions with Redis are determined based on what values I get back!


Solution

  • Firstly, don't close connect ions eagerly here. BookSleeve is designed to be used as a thread-safe multiplexer, taking load from any random number of callers at the same time. There are overheads in opening a connection. Now, there are a few ways a red is connection can become closed:

    • you electively close it
    • the server closes it (connection timeout, client fails to deal with backlog)
    • the client detects something on the inbound stream it isn't expecting

    My guess here is that the server has a connection timeout configured. I don't know about 1.1.*, but automatic timeout configuration detection and keep-alive "ping" messages were added at some point, which help. You can check the timeout via "config get timeout" in redis-cli. If this is the issue, you may find upgrading client version helps. In some builds you can also specify a timeout configuration manually (if you want to overrule the server).

    For unexpected data - that is unlikely, but there are a few events you can hook to detect, log and react to this.