Skip to content

Connection management

Val edited this page Sep 18, 2016 · 21 revisions

vtortola.RedisClient API has two fundamental parts:

  • RedisClient class handles the connection management. Usually you have one instance across all your AppDomain (or two instances if you have master/slave). It is a thread safe object, that usually is cached for the extend of your application lifetime.

  • IRedisChannel interface is used to execute commands. Channels are short lived, cheap to create, non-thread safe objects that represent virtual connections to Redis. Channels provide seamless access to commander and subscriber connections, analyze commands and decide how to route them through the three connection pools (multiplexed, exclusive and subscription).

Per connection management

Connections run initialization routines automatically right after connecting (or reconnecting).

  • Commander connections: Check for the existence of the required procedures and deploy the ones that do not exist.
  • Subscriber connections: Reestablish all the subscriptions of the channels that were operating through that connection if any.

They automatically reconnect in case of disconnection. RedisClient has a constructor overload the accepts a collection of IPEndPoint rather than a single one. Connections start by using the first one and in case of SocketException they choose the next endpoint in rota basis.

var endpoints = new [] 
{
    new IPEndPoint(IPAddress.Parse("10.0.0.1"), 6379), 
    new IPEndPoint(IPAddress.Parse("10.0.0.2"), 6379);
}
_client = new RedisClient(endpoints))
await _client.ConnectAsync(CancellationToken.None).ConfigureAwait(false);

There is possibility of running pre-initialization commands that will be executed even before than the initialization routines. For example, if the Redis instance requires authentication you must execute AUTH <password> in first place.

var options = new RedisClientOptions();
var authCommand = new PreInitializationCommand("auth @password", new { password = "yourpassword" });
options.InitializationCommands.Add(authCommand);

Multiple parameters of the connections can be tweaked throw the configuration options.

Connection pools

The client uses three connection pools:

  • Shared commander pool.
  • Exclusive commander pool.
  • Shared subscriber pool.

Which pool is selected for which command is done transparently by the channel.

Shared commander pool

It is a pool of TCP connections that are shared across all channels. When a command needs to be executed, first TCP idle connection or the one with less load is selected to execute the command and handle the result to the caller. Commands from multiple channels can be executed safely through the same TCP connection thanks to multiplexing and pipelining, so thousands of channels may be operating through a handful of TCP connections seamlessly.

The shared commander pool is configured through RedisClientOptions.MultiplexPoolOptions.CommandConnections, where an integer indicates how many connections you want to keep in the shared commander pool.

This is the faster commander pool.

Read more about available options.

####Exclusive commander pool It is a pool of TCP connections from which a connection can be selected for exclusive use, and return it when the use is done. There are situations where some commands, or sequence of commands, require to be executed in a individual connection. Channels are able of identifying these situations, hold a exclusive connection, and return to multiplex mode when done. Situations where an exclusive connection is used are:

  • The use or blocking operations, like BRPOP, BLPOP and BRPOPLPUSH. When the command returns the connection is returned to the exclusive pool.
  • Leaving open a transaction with MULTI. When EXEC or DISCARD is executed, the connection will be returned to the exclusive pool.
  • Leaving open a watch with WATCH. When EXEC, DISCARD or UNWATCH is executed, the connection will be returned to the exclusive pool.

Obviously the exclusive pool is less performant than the shared pool. The most of the times you will use the exclusive pool is because you need optimistic concurrency using WATCH like in this example. A way to prevent this is the use of LUA scripts, that in vtortola.RedisClient is done through procedures is preferred.

The shared commander pool is configured through RedisClientOptions.ExclusivePoolOptions, where two integers control the maximum and minimum number of connections you want to have in the shared pool.

Read more about available options.

Shared subscriber pool

It is another pool of TCP connections that are shared across all channels like the shared commander pool that also uses multiplexing and pipelining. The statements that contains Redis commands about subscription management are executed in the first TCP idle subscriber connection or the one with less load.

The channel holds this subscriber connection as its assigned subscriber connection for further subscription commands. Each subscriber connection manages which channels have subscriptions on going and is able to route the MESSAGE and PMESSAGE messages to the appropriate ones. One important feature of the subscriber connections, is that it aggregates the subscriptions of the channels it has been assigned too.

Read more about subscribing to topics.

Command routing

Depending on the structure of your command, statements can be routed to different connections seamlessly. A channel will route each statement to the appropriate connection and return a command execution result when all commands have been executed in their respective connections, so you always get a complete result.

The exclusive pool is disabled by default, since the aim of this component is to use procedures instead of WATCH or multiple round trips to the server. However, if you need to enable it, just give values to the exclusive pool options.

Note: for the sake of the examples, parameter binding is not being used, but performance wise is very unrecomended to use it this way. Read more about parameter binding.

Simple example

In this simple command, both statements will be sent to the shared commander pool. Remember that although they travel one after the other in the same connection, still Redis may interpolate other commands between them, so remember to use MULTI if you want to execute them like a single unit.

channel.Execute(@"
        incr keyA
        incr keyB
    ");

Simple example with subscription

In this example, first statement will be executed in the shared commander pool, the second will be executed in the shared subscriber pool. Also, the channel with remember the selected subscriber connection and will use it from now until IRedisChannel.Dispose() is called.

channel.Execute(@"
        incr keyA
        subscribe topicB
    ");

Exclusive example

This command will get a exclusive connection, execute the command and release it back to the pool when the command returns.

var result = channel.Execute("brpop listA");

Mixed example

This command will get a exclusive connection, execute the command and release it back to the pool when the command returns.

// Uses shared commander pool
channel.Execute("incr counter");

// Because the unclosed `WATCH`, a connection from the exclusive
// pool is used. The channel will hold it until it can be released.
var result = channel.Execute(@"
                     WATCH mykey
                     GET mykey");

if(result[1].GetString() != expectedValue)
{
   // Command is executed in the held exclusive connection.
   // After execution, connection is returned to the pool.
   channel.Execute("UNWATCH");
}
else
{
    // Command is executed in the held exclusive connection.
    // Because the final `EXEC` the connection is returned 
    // back to the exclusive pool.
    result = channel.Execute(@"
                     MULTI
                     SET mykey 'whatever value you want'
                     INCR ops
                     EXEC");
}

// Again the shared commander pool is used
channel.Execute("incr another:counter");
Clone this wiki locally