Пул соединений .NET не восстанавливается после короткого принудительного однопользовательского режима целевой БД

#c# #sql-server #connection-pooling

#c# #sql-сервер #объединение пулов соединений

Вопрос:

Наше приложение подключается к нашей БД, используя следующую строку подключения:

 Data Source=<IP>;Initial Catalog=<DBName>;User ID=<Account>;Password=<Password>
 

Он использует его, чтобы попытаться сохранить данные в цикле, который можно просто выполнить, следуя псевдокоду:

 bool stored;
do
{
    (using)(SqlConnection conn = new SqlConnection(StaticConnectionString))
    {
        try
        {
            conn.Open();
            //Create SP command and execute here
            stored = true;
        }
        catch(Exception e){/* Log and sleep few secs */}
    }

}while(!stored)
 

Мы столкнулись с некоторыми проблемами с блокировкой и чрезвычайно высоким дисковым вводом-выводом из-за неправильно разработанного индекса. Мы переводим БД в однопользовательский режим:

 ALTER DATABASE <DBName> SET SINGLE_USER WITH ROLLBACK IMMEDIATE
GO
 

И исправил проблему, а затем перевел БД в режим MULTI_USER.
После этого все остальные экземпляры нашего exe-файла нормально подключились к БД — однако exe, который пытался вставить довольно много данных (предыдущий цикл выполнялся в десятках параллельных потоков для разных записей), не восстанавливался в течение часа после этого (после чего мы перезапустили). При попытке подключения постоянно возникало следующее исключение:

 System.Data.SqlClient.SqlException (0x80131904): Cannot open database "<DBName>" requested by the login. The login failed.
Login failed for user '<Account>'.
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, UInt32 waitForMultipleObjectsTimeout, Boolean allowCreate, Boolean onlyOneCheckConnection, DbConnectionOptions userOptions, DbConnectionInternalamp; connection)
   at System.Data.ProviderBase.DbConnectionPool.TryGetConnection(DbConnection owningObject, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternalamp; connection)
   at System.Data.ProviderBase.DbConnectionFactory.TryGetConnection(DbConnection owningConnection, TaskCompletionSource`1 retry, DbConnectionOptions userOptions, DbConnectionInternalamp; connection)
   at System.Data.ProviderBase.DbConnectionClosed.TryOpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory, TaskCompletionSource`1 retry, DbConnectionOptions userOptions)
   at System.Data.SqlClient.SqlConnection.TryOpen(TaskCompletionSource`1 retry)
   at System.Data.SqlClient.SqlConnection.Open()
   at <Our Call to Connection.Open>
ClientConnectionId:9dcf1a45-0f53-4a0f-be8b-63d871ca0afd
 

Где все потоки сталкивались с проблемой для одного и того же внутреннего соединения (идентификатор guid пула подключений .NET framweork ClientConnectionId повторяется во всех трассировках стека из этой проблемы). В TCPView и process monitor я мог ясно видеть, что приложение даже не пытается создать новое соединение с сервером — оно просто каким-то образом внутренне запоминает, что соединение было прервано из-за режима SINGLE_USER, и повторно создает исключение при следующей попытке, и пул не пытается очистить такое соединение.
Я попытался принудительно выполнить несколько полных GC — без изменений, отсоединить присоединить базу данных — без изменений; запретить сетевой доступ к sql server и повторно включить его — без изменений.

Почему пул соединений не удаляет неудачные соединения? Как мы можем это исправить?

Комментарии:

1. Вы можете попробовать использовать SqlConnection. Метод ClearPool()

2. @PrzemekG Спасибо за предложение — я добавлю некоторый код для обнаружения этой проблемы и очистки пула. Однако остается вопрос — почему это происходит в первую очередь — я бы ожидал. Пул сетевых подключений, чтобы не объединять неудачные соединения