Пул соединений - один процесс - много потоков

Я понимаю пул соединений: если строка подключения точно такая же, мы повторно используем подключение, а не создаем новое.

Моя проблема в том, что я создаю много потоков для параллельной обработки. В этой «фиктивной» программе я создаю 500 потоков и позволяю функции ThreadPool обрабатывать их.

Шаги следующие:

  1. Каждый поток составляет таблицу обновления на SQL. (Указывается отметка времени для обновления)

  2. Затем поток находится в режиме ожидания от 1 до 10 секунд (произвольно).

  3. В конце поток выполняет еще одно обновление в SQL (указав временную метку для времени окончания)

  4. Затем выход из потока

    class Program
    {
        static void Main(string[] args)
        {
            int numberOfThreads = 150;
    
            ThreadPool.SetMinThreads(numberOfThreads, numberOfThreads);
            ThreadPool.SetMaxThreads(numberOfThreads, numberOfThreads);
    
            List<Int64> chunkList = new List<Int64>();
    
            int maxNumberOfChunks = 500;
            for (int i = 1; i < maxNumberOfChunks; i++)
            {
                chunkList.Add(i);
            }
    
            foreach (Int64 chunk_id in chunkList)
            {
                ThreadPool.QueueUserWorkItem(new WaitCallback(ThreadWorker), new arguments { chunk_id = chunk_id });
            }
            Console.ReadLine();
    
        }
        static void ThreadWorker(Object stateInfo)
        {
            arguments arguments = (arguments)stateInfo;
    
            Console.WriteLine("Chunk # : {0} is set to START", arguments.chunk_id);
            UpdateSQLdb(arguments.chunk_id, DateTime.Now, null, null, "START", null, null);
    
            Random random = new Random();
            int mseconds = random.Next(1, 10) * 1000;
            System.Threading.Thread.Sleep(mseconds);
            Console.WriteLine("Chunk # : {0} is sleeping for {1} sec.", arguments.chunk_id, mseconds);
    
            Console.WriteLine("Chunk # : {0} ist set to END", arguments.chunk_id);
            UpdateSQLdb(arguments.chunk_id, null, DateTime.Now, null, "END", null, null);
        }
        struct arguments
        {
            public Int64 chunk_id;
        }
    
        static void UpdateSQLdb(Int64 CHUNK_ID, DateTime? START_TS = null, DateTime? END_TS = null, Enum CHUNK_STATUS = null, string error_messages = null, byte? NEW_CALCULATION_ATTEMPTS = null, byte? NEW_POSTPROCESS_ATTEMPTS = null)
        {
            using (SqlConnection conn = new SqlConnection("Data Source=C55S01;Initial Catalog=MCS_BATCH;Integrated Security=SSPI;Asynchronous Processing=True")) //Timeout=60;Max Pool Size=200;Pooling=True;
            {
                int result = -1;
                conn.Open(); //<-- Each time I open a connection. It creates a new instead of reusing one from the ConnectionPool
    
                try
                {
                    using (SqlCommand cmd = new SqlCommand("TEST.UpdateSQL", conn))
                    {
                        cmd.CommandTimeout = 300; 
                        cmd.CommandType = System.Data.CommandType.StoredProcedure;
    
                        cmd.Parameters.Add("@CHUNK_ID", SqlDbType.BigInt, 15).Value = CHUNK_ID;
                        cmd.Parameters.Add("@START_TS", SqlDbType.DateTime2, 7).Value = START_TS;
                        cmd.Parameters.Add("@END_TS", SqlDbType.DateTime2, 7).Value = END_TS;
                        cmd.Parameters.Add("@ERR_MESSAGE", SqlDbType.VarChar).Value = error_messages;
                        cmd.Parameters.Add("@ReturnValue", System.Data.SqlDbType.Int, 4).Direction = System.Data.ParameterDirection.ReturnValue;
    
                        try
                        {
                            result = cmd.ExecuteNonQuery();
    
                            int return_value = (int)cmd.Parameters["@ReturnValue"].Value;
                            if (return_value != 0)
                            {
                                Console.WriteLine("1. Error in running TEST.UpdateSQL, return value is : {0}", cmd.Parameters["@ReturnValue"].Value);
                            }
                        }
                        catch (SqlException ex)
                        {
                            UpdateSQLdb(CHUNK_ID, null, DateTime.Now, null, ex.Message.ToString(), null, null);
                            Console.WriteLine("2. Error executing TEST.UpdateSQL : {0}", ex);
                        }
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine("3.Error in TEST.UpdateSQL : {0}", ex);
                    throw;
                }
                if (conn.State == ConnectionState.Open)
                {
                    Console.WriteLine("Closing connection....");
                    conn.Close();
                }
                conn.Dispose();
            }
        }
    }
    

    }

Моя проблема в том, что я получаю System.InvalidOperationException was unhandled (Timeout expired. Время ожидания истекло до получения соединения из пула. Это могло произойти из-за того, что все соединения из пула использовались достигнут максимальный размер пула.)

Я отслеживал количество подключений на SQL-сервере, и он быстро достиг 100 подключений (что является максимальным количеством подключений по умолчанию в пуле).

Если вы пытались добавить эти параметры в строку подключения: Timeout = 60 "Max Pool Size = 200; Pooling = True;

Но это просто отложит проблему на более поздний этап, потому что пул соединений достигнет 200 и тайм-аут будет достигнут в какой-то момент.

Вопрос: почему подключения создаются снова и снова, вместо того, чтобы повторно использовать одно из пула подключений?

Любые подсказки, подсказки и советы приветствуются.


person Theis Kristensen    schedule 20.02.2014    source источник
comment
Вы пытаетесь запустить 500 одновременных процессов на 100 подключениях. Как вы думаете, это возможно? Пул соединений SQL не может одновременно запускать две (три или четыре) разные команды в одном соединении. Это не то, что означает пул соединений.   -  person EkoostikMartin    schedule 20.02.2014
comment
В любом случае маловероятно, что ваше сетевое соединение действительно способно справиться с таким количеством одновременных запросов. У вас почти наверняка не должно быть такого количества потоков или такого количества соединений, если только это не какой-то большой сервер с большим количеством ресурсов, поддерживающих его.   -  person Servy    schedule 20.02.2014
comment
Это хороший вопрос. После использования безопасно утилизируйте соединение. Вы используете объект подключения только на время одного вызова. Объединение должно работать нормально. При подключении из пула может истекать тайм-аут. Это не просто сбой, если нет подключения. Это должно работать идеально, если все соединения удерживаются в течение небольшого промежутка времени. Разберитесь, почему связи держатся долго.   -  person usr    schedule 21.02.2014
comment
Несвязанный: вы используете суеверный шаблон утилизации. Трехкратная утилизация вместо одного не принесет никакой пользы.   -  person usr    schedule 21.02.2014


Ответы (1)


Он делает именно то, что вы просили. Он использует соединения из пула, но вы приложили слишком много усилий. Если у вас 500 потоков и 200 подключений, у каждого потока не может быть подключения. Вероятно, у вас должно быть столько же подключений, сколько потоков.

Если у вас есть еще больше работы (все 500 потоков заняты), вы должны либо вернуть сообщение об ошибке потребителю, либо иным образом ограничить ввод в ваше приложение.

person Geoff Genz    schedule 20.02.2014