J'ai une application qui se connecte à SQL Server. Actuellement, je reçois,
Timeout expired. The timeout period elapsed prior to obtaining a connection from the pool. This may have occurred because all pooled connections were in use and max pool size was reached.
Quand j'ai couru,
SELECT
DB_NAME(dbid) as DBName,
COUNT(dbid) as NumberOfConnections,
loginame as LoginName
FROM
sys.sysprocesses
WHERE
DB_NAME(dbid) ='MyDb'
GROUP BY
dbid, loginame
DBName NumberOfConnections LoginName
MyDb 10 sa
MyDb 109 MyUser
Le statut de tous les processus est sleeping
et cms est AWAITING COMMAND
Voici mon code,
private async Task<object> ExecuteAsync<T>(ExecutionType executionType, CommandType commandType, string commandText, IsolationLevel isolationLevel, SqlParameter[] parameters, Func<IDataReader, T> callback = null)
{
var stopwatch = new Stopwatch();
stopwatch.Start();
using (var connection = new SqlConnection(_settings.DatabaseConnectionString))
{
using (var command = new SqlCommand(commandText, connection) {CommandType = commandType})
{
command.Parameters.AddRange(parameters);
await connection.OpenAsync().ConfigureAwait(false);
command.CommandTimeout = _settings.CommandTimeout;
var transaction = connection.BeginTransaction(isolationLevel);
command.Transaction = transaction;
try
{
object result;
switch (executionType)
{
case ExecutionType.Reader:
var reader = await command.ExecuteReaderAsync().ConfigureAwait(false);
using (reader)
{
var list = new List<T>();
while (reader.Read())
{
if (callback != null)
{
var item = callback(reader);
if (item != null)
{
list.Add(item);
}
}
}
result = list;
}
break;
case ExecutionType.NonQuery:
result = await command.ExecuteNonQueryAsync().ConfigureAwait(false);
break;
default:
result = await command.ExecuteScalarAsync().ConfigureAwait(false);
break;
}
transaction.Commit();
stopwatch.Stop();
var elapsed = stopwatch.Elapsed;
if (elapsed.Seconds > 2)
{
_logger.Log(string.Format("{0} took {1} time", command.CommandText, elapsed));// only log if it tooks more than 2 seconds
}
return result;
}
catch (Exception exception)
{
_logger.Log(exception);
transaction.Rollback();
throw;
}
}
}
}
Il semble que vous ayez une application qui ne ferme pas ou ne supprime pas correctement les objets SqlConnection
. Par défaut, SqlConnection
a une taille maximale de pool de 1 .
Le correctif serait de travailler avec l'application pour savoir pourquoi les connexions ne sont pas nettoyées, car elles sont toujours "actives" dans le pool de connexions particulier, c'est pourquoi vous ne pouvez pas en saisir une autre, car il n'y a pas connexions inactives dans le pool à utiliser.
L'application doit effectuer un appel à SqlConnection.Close()
ou SqlConnection.Dispose()
afin de libérer la connexion et de la marquer comme "inactive".
J'ai écrit un vaste article de blog ( Pooling de connexions pour le DBA SQL Server ) sur le pool de connexions , et cela devrait faire la lumière pourquoi vous voyez ce que vous voyez, ainsi que la correction programmatique du problème.
Une alternative est que si vous traitez un volume plus élevé d'enregistrements, vous pouvez en fait obtenir une erreur trompeuse. Il se peut que vous ayez utilisé tous vos ports sur votre PC, vous pouvez le vérifier en exécutant Sysinternals TCPView .
Si vous voyez des milliers de TIME_WAIT , c'est une cause possible.
En substance, une fois que votre application a fermé sa connexion à la base de données (à la fin de using (var connection = new SqlConnection(_settings.DatabaseConnectionString))
), TCP attendra près de 4 minutes avant que le port utilisé était libre de être utilisé à nouveau.
De la mémoire, vous devez traiter environ 16 000 exécutions en moins de 4 minutes pour que cela se produise (cela dépendra de la version de votre système d'exploitation et de ce que vous avez d'autre sur ce PC).