Connection Pooling: Implemention

time to read 7 min | 1376 words

Given the following contact:

/// <summary>
/// Thread Safety - This is NOT a thread safe connection
/// Exception Safety - After an exception is thrown, it should be disposed and not used afterward
/// Connection Pooling - It is expected that this will be part of a connection pool
/// </summary>
public class DistributedHashTableStorageClient

I decided that I needed to really didn’t want to pass the responsibility for that to the client, and that I wanted to handle that inside my library. Here is what I came up with:

public class DefaultConnectionPool
{
    private static readonly ILog log = LogManager.GetLogger(typeof (DefaultConnectionPool));
    readonly object locker = new object();

    private readonly Dictionary<NodeEndpoint, LinkedList<PooledDistributedHashTableStorageClientConnection>> pooledConnections =
        new Dictionary<NodeEndpoint, LinkedList<PooledDistributedHashTableStorageClientConnection>>();

    public IDistributedHashTableStorage Create(NodeEndpoint endpoint)
    {
        PooledDistributedHashTableStorageClientConnection storage = null;
        lock (locker)
        {
            LinkedList<PooledDistributedHashTableStorageClientConnection> value;
            if (pooledConnections.TryGetValue(endpoint, out value) && value.Count > 0)
            {
                storage = value.First.Value;
                value.RemoveFirst();
            }
        }
        if (storage != null)
        {
            if (storage.Connected == false)
            {
                log.DebugFormat("Found unconnected connection in the pool for {0}", endpoint.Sync);
                try
                {
                    storage.Dispose();
                }
                catch (Exception e)
                {
                    log.Debug("Error when disposing unconnected connection in the pool", e);
                }
            }
            else
            {
                return storage;
            }
        }
        log.DebugFormat("Creating new connection in the pool to {0}", endpoint.Sync);
        return new PooledDistributedHashTableStorageClientConnection(this, endpoint);
    }

    private void PutMeBack(PooledDistributedHashTableStorageClientConnection connection)
    {
        lock (locker)
        {
            LinkedList<PooledDistributedHashTableStorageClientConnection> value;
            if (pooledConnections.TryGetValue(connection.Endpoint, out value) == false)
            {
                pooledConnections[connection.Endpoint] = value = new LinkedList<PooledDistributedHashTableStorageClientConnection>();
            }
            value.AddLast(connection);
        }
        log.DebugFormat("Put connection for {0} back in the pool", connection.Endpoint.Sync);
    }

    class PooledDistributedHashTableStorageClientConnection : DistributedHashTableStorageClient
    {
        private readonly DefaultConnectionPool pool;

        public PooledDistributedHashTableStorageClientConnection(
            DefaultConnectionPool pool,
            NodeEndpoint endpoint) : base(endpoint)
        {
            this.pool = pool;
        }

        public bool Connected
        {
            get { return client.Connected; }
        }

        public override void Dispose()
        {
            if(Marshal.GetExceptionCode() != 0)//we are here because of some sort of error
            {
                log.Debug("There was an error during the usage of pooled client connection, will not return it to the pool (may be poisioned)");
                base.Dispose();
            }
            else if(Connected == false)
            {
                log.Debug("The connection was disconnected, will not return connection to the pool");
                base.Dispose();
            }
            else
            {
                pool.PutMeBack(this);
            }
        }
    }
}

I think that should pretty much cover everything I need.

Thoughts?