Подтвердить что ты не робот

Несколько одновременных тайм-аутов подключения к сети в многопоточной службе Windows

У меня есть многопоточная служба Windows, которую я разработал с VS 2010 (.NET 4.0), которая может иметь от нескольких до нескольких десятков потоков, каждый из которых извлекает данные с медленного сервера через Интернет, а затем использует локальную базу данных для записи этих данных (поэтому процесс связан с Интернетом, а не с привязкой к локальной сети или ЦП).

С некоторой регулярностью я получаю поток /flurry/burst следующей ошибки из нескольких потоков одновременно:

System.Data.SqlClient.SqlException(0x80131904): время ожидания истекло. Период ожидания истекает до завершения операции или сервер не отвечает.

Стек вызова для этой ошибки обычно:

в System.Data.ProviderBase.DbConnectionPool.GetConnection(DbConnection owningObject)

в System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)

в System.Data.ProviderBase.DbConnectionClosed.OpenConnection(внешнее соединение DbConnection, DbConnectionFactory connectionFactory)

в System.Data.SqlClient.SqlConnection.Open()

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

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

using (SqlConnection con = new SqlConnection(GetConnectionString()))
using (SqlCommand cmd = new SqlCommand("AddGdsMonitorLogEntry", con))
{
    cmd.CommandType = CommandType.StoredProcedure;

    /* setting cmd.Parameters [snipped] */

    // We have been getting some timeouts writing to the log; wait a little longer than the default.
    cmd.CommandTimeout *= 4;

    con.Open();

    cmd.ExecuteNonQuery();
}

Большое спасибо!

ИЗМЕНИТЬ

Учитывая комментарии к этому в зеркальных средах, я должен действительно упомянуть, что данная база данных зеркалирована. Он обозначен в SSMS как "Принципал, Синхронизированный" в "Высокая безопасность без автоматического переключения (синхронный)".

РЕДАКТИРОВАТЬ 5/26/11

Я ничего не вижу в журналах SQL Server, чтобы указать на какие-либо проблемы. (У меня нет доступа к средству просмотра событий Windows на этом сервере, но я попросил кого-то найти меня.)

4b9b3361

Ответ 1

В соответствии с Сообщение в блоге MSDN, созданное сегодня (ура для Google!):

Microsoft подтвердила, что это проблема в текущей версии ADO.NET. Эта проблема будет исправлена ​​в версии ADO.NET, поставляется с Visual Studio 2011.

Тем временем мы просим использовать следующие обходные пути:

  • Увеличьте время ожидания строки подключения до 150 секунд. Это даст первой попытке достаточно времени для подключения (150 *.08 = 12 секунд)

  • Добавьте MinPool Size = 20 в строку подключения. Это всегда будет поддерживать минимум 20 соединений в пуле, и будет меньше шансов на создание нового соединения, что уменьшит вероятность этой ошибки.

  • Повысить производительность сети. Обновите драйверы NIC до последней версии прошивки. Мы видели задержку сети, когда ваша карта NIC несовместима с некоторыми настройками масштабируемого сетевого пакета. Если вы используете Windows Vista с пакетом обновления 1 (SP1) или выше, вы также можете отключить автоматическую настройку окна получения. Если у вас включено объединение NIC, отключить его будет хорошим вариантом.

Сама посылка представляет собой интересное чтение, говорящее о попытке повторного использования соединения TCP/IP. И слава всем людям, которые сказали: "Эй, похоже, это связано с зеркалированием..."! И обратите внимание на комментарий об этом: "из-за медленного ответа от SQL Server или из-за сетевых задержек".

UGH!!!

Спасибо всем, кто опубликовал. Теперь мы все должны попросить заплату на .NET Framework (или какой-либо другой механизм исправления ADO.NET), поэтому нам не нужно ждать (и покупать) Visual Studio 11...

Ответ 2

Тайм-аут подключения - это другое дело, чем таймаут команды. Тайм-аут команды применяется к ситуации, когда установлено соединение, но из-за некоторых внутренних причин сервер не может вернуть никаких результатов в течение необходимого времени. Таймаут по умолчанию составляет 30 секунд. http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlcommand.commandtimeout.aspx

Попробуйте указать время ожидания соединения в строке подключения. Значение по умолчанию - 15 секунд, что может быть причиной проблемы, которую вы видите. Вы также можете указать время ожидания соединения в коде: http://msdn.microsoft.com/en-us/library/system.data.sqlclient.sqlconnection.connectiontimeout.aspx

Ответ 3

Я получаю это время от времени на этом старом сервере базы данных, который у нас есть (сейчас наступает 10 лет). Когда это происходит, хотя это потому, что что-то забивает эту вещь с помощью соединений/запросов постоянно. Я предполагаю, что вы обнаружите, что когда это происходит, сервер базы данных находится под нагрузкой (или большое количество подключений или что-то в этом роде). В любом случае, по моему опыту, если вы можете оптимизировать код, оптимизировать базу данных, получить более четкую сервер базы данных и т.д. все это помогает. Еще одна вещь, которую вы можете сделать, которую предлагает Петр, - это просто перерыв в соединении. Я все равно продолжаю и оптимизирую некоторые вещи, хотя (должен помочь в конечном итоге).

Ответ 4

Я смог несколько надежно воспроизвести эту проблему. У меня есть служба, которая, когда запрашивается задание на обработку, запускает обработку в новом appdomain/thread. Этот поток будет выполнять от 10 до 16 запросов базы данных одновременно. Когда я запускаю 30 из этих заданий один за другим, случайное одно или два из заданий будут сбой с ошибкой таймаута.

Я изменил строку подключения, чтобы отключить пул соединений с пулом = false, а затем ошибка изменилась на следующую. Это забрасывается 3 или 4 раза внутри агрегатного исключения, поскольку соединения происходят внутри Parallel.For

System.Data.SqlClient.SqlException: Timeout expired.  The timeout period elapsed prior to completion of the operation or the server is not responding.
   at System.Data.SqlClient.SqlInternalConnection.OnError(SqlException exception, Boolean breakConnection)
   at System.Data.SqlClient.TdsParser.ThrowExceptionAndWarning()
   at System.Data.SqlClient.TdsParserStateObject.ReadSniError(TdsParserStateObject stateObj, UInt32 error)
   at System.Data.SqlClient.TdsParserStateObject.ReadSni(DbAsyncResult asyncResult, TdsParserStateObject stateObj)
   at System.Data.SqlClient.TdsParserStateObject.ReadNetworkPacket()
   at System.Data.SqlClient.TdsParser.ConsumePreLoginHandshake(Boolean encrypt, Boolean trustServerCert, Boolean& marsCapable)
   at System.Data.SqlClient.TdsParser.Connect(ServerInfo serverInfo, SqlInternalConnectionTds connHandler, Boolean ignoreSniOpenTimeout, Int64 timerExpire, Boolean encrypt, Boolean trustServerCert, Boolean integratedSecurity)
   at System.Data.SqlClient.SqlInternalConnectionTds.AttemptOneLogin(ServerInfo serverInfo, String newPassword, Boolean ignoreSniOpenTimeout, TimeoutTimer timeout, SqlConnection owningObject)
   at System.Data.SqlClient.SqlInternalConnectionTds.LoginWithFailover(Boolean useFailoverHost, ServerInfo primaryServerInfo, String failoverHost, String newPassword, Boolean redirectedUserInstance, SqlConnection owningObject, SqlConnectionString connectionOptions, TimeoutTimer timeout)
   at System.Data.SqlClient.SqlInternalConnectionTds.OpenLoginEnlist(SqlConnection owningObject, TimeoutTimer timeout, SqlConnectionString connectionOptions, String newPassword, Boolean redirectedUserInstance)
   at System.Data.SqlClient.SqlInternalConnectionTds..ctor(DbConnectionPoolIdentity identity, SqlConnectionString connectionOptions, Object providerInfo, String newPassword, SqlConnection owningObject, Boolean redirectedUserInstance)
   at System.Data.SqlClient.SqlConnectionFactory.CreateConnection(DbConnectionOptions options, Object poolGroupProviderInfo, DbConnectionPool pool, DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionFactory.CreateNonPooledConnection(DbConnection owningConnection, DbConnectionPoolGroup poolGroup)
   at System.Data.ProviderBase.DbConnectionFactory.GetConnection(DbConnection owningConnection)
   at System.Data.ProviderBase.DbConnectionClosed.OpenConnection(DbConnection outerConnection, DbConnectionFactory connectionFactory)
   at System.Data.SqlClient.SqlConnection.Open()
   at Tps.PowerTools.CoreEngine.V5.DataAccess.DataContext.ExecuteQuery(PtQuery query, ValueStore`1 store, String readerDescription) in C:\SourceCode\Tps.PowerToolsV1\Trunk\Libraries\CoreEngine\CoreEngine.V5\DataAccess\DataContext.cs:line 326
   at Tps.PowerTools.CoreEngine.V5.DataAccess.DataContext.<StockHistoricalData>b__15(PtQuery query) in C:\SourceCode\Tps.PowerToolsV1\Trunk\Libraries\CoreEngine\CoreEngine.V5\DataAccess\DataContext.cs:line 302
   at System.Threading.Tasks.Parallel.<>c__DisplayClass32`2.<PartitionerForEachWorker>b__30()
   at System.Threading.Tasks.Task.InnerInvokeWithArg(Task childTask)
   at System.Threading.Tasks.Task.<>c__DisplayClass7.<ExecuteSelfReplicating>b__6(Object )

Ответ 5

Оптимизация запросов, выполняемых на удаленном сервере, всегда поможет. Время каждого запроса и искать длинные. Если вы просто читаете, используйте (NOLOCK) подсказку в операторах SELECT. Для меня это была спасательная жизнь. Просто прочитайте его, чтобы убедиться, что он подходит в вашем приложении. Если у вас есть доступ к удаленной базе данных, убедитесь, что индексы не фрагментированы. Это приведет к серьезному замедлению выполнения запроса. Убедитесь, что индексы перестроены/реорганизованы как часть плана обслуживания SQL. При необходимости добавьте новые индексы.

Продление тайм-аута может ухудшить ситуацию. Если вы позволяете запросам работать дольше, потенциально, больше запросов будет тайм-аут. Тайм-аут должен защищать сервер и других клиентов, обращающихся к нему. Сбивать его немного - это не огромная сделка, но вы не хотите, чтобы запросы, запущенные в течение долгого времени, убивали сервер.