Пользовательский пул соединений для поставщика Exasol Ado.Net

Мы используем базу данных в памяти Exasol, которая предоставляет поставщика Ado.Net, но, похоже, отсутствуют некоторые важные функции, такие как ConnectionPooling, поэтому каждое соединение создается и уничтожается по запросу, что влияет на нашу производительность при подключении. в размещенную базу данных на AWS. Я создал простой ConnectionPool с возможностью Resize, пожалуйста, предложите, будет ли это служить цели или мне нужно сделать что-то еще.

Обратите внимание, что я не ищу обзор кода, а критический анализ того, чего мне может не хватать в текущей реализации, а также если есть доступная реализация (Nuget, Git), которую я могу использовать повторно. В настоящее время я изменяю размер в зависимости от размера, как добиться того же в зависимости от времени, когда определенная продолжительность простоя должна очищать несколько ресурсов из очереди, тем самым уменьшая размер.

Важные детали:

  1. Используйте ConcurrentQueue внутри для потокобезопасного доступа к ресурсам с нескольких клиентов.
  2. Используйте AutoResetEvent для ожидания и сигнала, если пул пуст
  3. Используйте TPL для операций изменения размера, не останавливая вызывающий код, насколько я понимаю, это работает, даже когда клиентский вызов возвращается, как на Threadpool thread

    class ExasolConnectionPool
    {
        /// <summary>
        /// Thread safe queue for storing the connection objects
        /// </summary>
        private ConcurrentQueue<EXAConnection> ExasolConnectionQueue { get; set; }
    
        /// <summary>
        /// Number of connections on the Connection pool
        /// </summary>
        private int _connectionCount;
    
        /// <summary>
        /// Max Pool Size
        /// </summary>
        private int MaxPoolSize { get; set; }
    
        /// <summary>
        /// Min Pool Size
        /// </summary>
        private int MinPoolSize { get; set; }
    
        /// <summary>
        /// Increase in Pool Size
        /// </summary>
        private int IncreasePoolSize { get; set; }
    
        /// <summary>
        /// Decrease in Pool Size
        /// </summary>
        private int DecreasePoolSize { get; set; }
    
        /// <summary>
        /// Connection string for the Connection pool connections
        /// </summary>
        private string ConnectionString { get; set; }
    
        /// <summary>
        /// Auto Reset event for the connection pool
        /// </summary>
        private AutoResetEvent ExasolConnectionPoolAre { get; set; }
    
        /// <summary>
        /// Connection pool specific Lock object
        /// </summary>
        private readonly object lockObject;
    
        /// <summary>
        /// Connection pool constructor
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="poolSize"></param>
        public ExasolConnectionPool(string connectionString, int poolSize = 10)
        {
            // Set the Connection String
            ConnectionString = connectionString;
    
            // Intialize the Connection Queue
            ExasolConnectionQueue = new ConcurrentQueue<EXAConnection>();
    
            // Enqueue initial set of connections
            for (int counter = 0; counter < poolSize; counter++)
            {
                var exaConnection = new EXAConnection {ConnectionString = ConnectionString};
    
                ExasolConnectionQueue.Enqueue(exaConnection);
            }
    
            // Initialize Lock object 
            lockObject = new object();
    
            // Set the Connection queue count
            _connectionCount = poolSize;
    
            // Max pool size
            MaxPoolSize = poolSize;
    
            // Min Pool Size
            MinPoolSize = 2;
    
            IncreasePoolSize = 5;
    
            DecreasePoolSize = 3;
    
            ExasolConnectionPoolAre = new AutoResetEvent(false);
        }
    
        /// <summary>
        /// 
        /// </summary>
        /// <returns></returns>
        public EXAConnection GetConnection()
        {
            // Return ExaConnection object
            EXAConnection returnConnection;
    
            // Try Dequeue the connection object from the Concurrent Queue
            var validExasolConnection = ExasolConnectionQueue.TryDequeue(out returnConnection);
    
            // If No Valid connection is available, then wait using AutoReset signaling mechanism
            while (!validExasolConnection)
            {
                ExasolConnectionPoolAre.WaitOne();
    
                validExasolConnection = ExasolConnectionQueue.TryDequeue(out returnConnection);
            }
    
            // Thread safe connection count update
            Interlocked.Decrement(ref _connectionCount);
    
            Task.Factory.StartNew(() =>
            {
                lock (lockObject)
                {
                    if (_connectionCount > MinPoolSize) return;
    
                    for (var counter = 0; counter < IncreasePoolSize; counter++)
                    {
                        var exaConnection = new EXAConnection {ConnectionString = ConnectionString};
    
                        ExasolConnectionQueue.Enqueue(exaConnection);
    
                        Interlocked.Increment(ref _connectionCount);
                    }
                }
            });
    
            return (returnConnection);
        }
    
        /// <summary>
        /// 
        /// </summary>
        /// <param name="returnedConnection"></param>
        public void ReturnConnection(EXAConnection returnedConnection)
        {
            ExasolConnectionQueue.Enqueue(returnedConnection);
    
            Interlocked.Increment(ref _connectionCount);
    
            ExasolConnectionPoolAre.Set();
    
            Task.Factory.StartNew(() =>
            {
                lock (lockObject)
                {
                    if (_connectionCount < MaxPoolSize * 1.5) return;
    
                    for (var counter = 0; counter < DecreasePoolSize; counter++)
                    {
                        EXAConnection exaConnection;
    
                        if (ExasolConnectionQueue.TryDequeue(out exaConnection))
                        {
                            exaConnection.Dispose();
    
                            exaConnection = null;
    
                            Interlocked.Decrement(ref _connectionCount);
                        }
                    }
                }
            });
        }
    }
    

person Mrinal Kamboj    schedule 08.09.2016    source источник


Ответы (1)


Реализация для вашего пула в порядке. Я не знаю о каких-либо реализациях NuGet, которые были бы такими маленькими и не слишком сложными для вашего случая. Я просто хочу добавить небольшое количество предложений, которые вы можете исследовать самостоятельно.

  1. # P2 #
    # P3 #
    # P4 #
  2. В общем, логика изменения размера выполняется простым способом, с удвоением размера, поэтому, если вы достигли предела, размер стал вдвое больше, и наоборот для уменьшения. Я думаю, что предоставление пользователям возможности управлять этими константами может привести к некоторым странным ошибкам, таким как отрицательный размер пула и тому подобное.

    Таким образом, вы должны установить свои свойства как private и, как по мне, удалить свойства, касающиеся изменения размера. Возможно, в будущем вы сможете собирать статистику среднего размера пула для вашего приложения и использовать этот параметр по умолчанию.

person VMAtm    schedule 08.09.2016
comment
Спасибо за отзыв и важные подробности. Для изменения размера, поскольку объект соединения является дорогостоящим ресурсом, поэтому, в отличие от стандартных пулов ресурсов, предпочтительнее механизм контролируемого изменения размера, иногда простое открытое соединение сверх определенной продолжительности является пустой тратой ресурсов, которые можно воссоздать. В других пулах ресурсов они также увеличивают размер коллекции за счет удвоения, а не фактических объектов, которые все еще необходимо создавать/добавлять по мере необходимости. - person Mrinal Kamboj; 09.09.2016
comment
Для различных свойств, как вы могли заметить, они являются частными объектами, но, как и любой стандартный пользователь пула соединений, должен иметь возможность настраивать его с определенными проверками, чтобы избежать исключений. - person Mrinal Kamboj; 09.09.2016
comment
Да, это было просто наблюдение. Я думаю, вы знаете, что делаете, так что удачи с вашим проектом :) - person VMAtm; 09.09.2016