Несколько одновременных тайм-аутов подключения к сети в многопоточной службе 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 на этом сервере, но я попросил кого-то найти меня.)
Ответы
Ответ 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. При необходимости добавьте новые индексы.
Продление тайм-аута может ухудшить ситуацию. Если вы позволяете запросам работать дольше, потенциально, больше запросов будет тайм-аут. Тайм-аут должен защищать сервер и других клиентов, обращающихся к нему. Сбивать его немного - это не огромная сделка, но вы не хотите, чтобы запросы, запущенные в течение долгого времени, убивали сервер.