web-dev-qa-db-fra.com

Fournisseur de données Oracle pour .NET: la demande de connexion a expiré

Nous avons un service Web C # WCF hébergé sur Windows 2008 SP2/IIS 7 et accédant à une base de données Oracle. Habituellement, l'accès aux données fonctionne bien, mais lors des tests de charge, il arrive souvent à expiration, les journaux et les exceptions indiquent:

Error occurred when processing XXXXXXXX Web Service
Oracle.DataAccess.Client.OracleException Connection request timed out at Oracle.DataAccess.Client.OracleException.HandleErrorHelper(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, OpoSqlValCtx* pOpoSqlValCtx, Object src, String procedure, Boolean bCheck)
   at Oracle.DataAccess.Client.OracleException.HandleError(Int32 errCode, OracleConnection conn, IntPtr opsErrCtx, Object src)
   at Oracle.DataAccess.Client.OracleConnection.Open()
   at MyWorkspace.WorkForceDataAccess.CheckStaffIdInRSW()
   at MyWorkspace.MyClass.MyFunction(MyDataType MyData)

Pour interroger la base de données, nous utilisons quelque chose comme ceci:

OracleConnection orConn = new OracleConnection();
orConn.ConnectionString = "user id=xxx; password=xxx; Connection Timeout=600; Max Pool Size=150; data source= (DESCRIPTION =(ADDRESS = (PROTOCOL = TCP)(Host = MYHOST.MYDOMAIN.com)(PORT = 1771)) (CONNECT_DATA =(SERVER = DEDICATED)(SERVICE_NAME = MYSERVICE.MYDOMAIN.com)))";
orConn.Open();

using (var cmd = new OracleCommand("MY_UTIL.check_StaffIdInRSW", orConn) { CommandType = CommandType.StoredProcedure })
{
    cmd.Parameters.Add("P_Staff_Id", OracleDbType.Int32);
    cmd.Parameters["P_Staff_Id"].Direction = ParameterDirection.Input;
    cmd.Parameters["P_Staff_Id"].Value = Convert.ToInt32(MyDataObject.StaffId);

    cmd.Parameters.Add("P_retvalue", OracleDbType.Int32);
    cmd.Parameters["P_retvalue"].Direction = ParameterDirection.Output;

    cmd.ExecuteNonQuery(); // Execute the function

    //obtain result
    returnVal = int.Parse(cmd.Parameters["P_retvalue"].Value.ToString());
}

Je suis assez confiant que la procédure stockée qui est appelée ne prend pas tout le temps. C'est une procédure assez simple qui vérifie rapidement si le P_Staff_Id existe dans la table et renvoie le résultat.

En outre, cela se produit uniquement lors des tests de charge. En temps normal, tout va bien, mais lors de fortes charges, avec 1 message par seconde, cela se produit après une certaine fluidité.

Pour contourner le problème, j'ai ajouté "Délai de connexion = 600; Taille maximale du pool = 150" à la chaîne de connexion, mais cela n'a pas résolu le problème.

La même application est exécutée sur un serveur de développement et tout fonctionne correctement. Nous n'avons jamais rencontré ce problème là-bas.

Toute suggestion sur ce qu'il faut essayer serait appréciée. On dirait que je suis à court d'options.

6
DjD

Nous avons eu un problème similaire, et il a fallu un certain temps pour résoudre ce problème et le résoudre. Notre code sur le stress avec de nombreux fichiers d'entrée et de nombreux threads traitant, chaque thread utilisant Entity Framework et ouvrant une connexion Oracle Db, et effectuant un ensemble de requêtes et d'insertions de base de données, permet de archiver occasionnellement. Mais fonctionne la plupart du temps.

J'ai modifié le constructeur DbContext pour ouvrir explicitement OracleConnection. J'ai ajouté du code comme celui-ci

for (i = 0; i < 5; i++)
   try {
       oracleConnection.Open();
   } catch (OracleException) {
     Sleep for 15 ms and retry. 
     On last attempt I also do OracleConnection.ClearAllPools()
   }

Cela s’est amélioré, mais ne l’a toujours pas résolu complètement. Je me suis cassé la capture du débogueur et j'ai vu que beaucoup de threads essayent de s'ouvrir et que peu de threads sont en cours de traitement. À l'ouverture de la pile Oracle, Oracle, à des fins internes, exécute ThreadPool.QueueUserWorkItem et attend son achèvement. Je peux voir au-dessus de la pile son attente. Ici, de nombreuses connexions en pool sont disponibles (la valeur par défaut est 100). J'utilise à peine 10 unités. Il ne manque donc pas de ressources.

Mais le problème est dans notre code et nous avons également utilisé ThreadPool.QueueUserWorkItem sans limitation supplémentaire. Je pensais que c'était cool de simplement faire la queue pour tous les travaux que nous devons faire, tout ce dont nous avons besoin pour cela, et de laisser .NET s'en occuper. Mais cela a un problème subtil. Tous nos travaux ont consommé le nombre total de files d'attente. Lorsque OracleConnection souhaite obtenir une connexion en pool à partir du pool, il se met également en file d'attente dans le pool de threads. Mais cela ne va jamais se terminer. Nos travaux attendent tous OracleConnection.Open et son processus Thread en file d'attente sera toujours en file d'attente. Alors finalement, l'attente se terminera par timeout. Il est regrettable que, même s'il existe une multitude de connexions en pool, nous ayons consommé toutes les procédures ThreadPool, et que le threadpool d'Oracle n'ait même pas eu sa chance. Ici, définir ThreadPool.SetMaxThreads ne va pas non plus aider. Le problème est toujours le même. Nous accaparons toutes les ressources de pool de threads et Orcale ne va pas en trouver une et sera toujours en file d'attente.

Le correctif n’est pas de compter uniquement sur ThreadPool, nous ajoutons également notre propre limitation. J'ai utilisé BlockingCollection et sempahores et n'ajouté qu'un nombre limité de tâches simultanées dans ThreadPool, par exemple 5. Ainsi, OracleConnection trouvera toujours un thread ThreadPool disponible et échouera.

5
R. Sridharan

Même j’avais l’habitude d’obtenir ce problème plus souvent, même après l’utilisation de Connection.Close ()

Après une longue analyse, j'ai appris peu de choses comme mentionné ci-dessous

  1. Connection.Close () ne supprime pas la connexion à la base de données
  2. Délai de connexion ne signifie pas que le problème concerne uniquement les requêtes de base de données
  3. Le délai de connexion peut également être dû à des connexions exhaustives dans le pool de connexions (qui était le coupable pour moi car il atteignait le nombre maximal de sessions de la connexion à la base de données)

Correctif: - L'analyse a pris beaucoup de temps, mais le correctif n'a duré que 2 minutes.

using(DbConnection instance)
{

}

Par exemple :-

using (DbConnection  objDbConnection = new DbConnection())
{
   ojDbConnection.PersistData();
}

Sous PersistData (); Toutes les opérations de base de données telles que Open, close e.tc. sera réalisée

Comme nous le savons tous, "utiliser" est une forme abrégée de

try
{

}
catch()
{

}
Finally
{
  Dispose objDbConnection;
}

J'espère que ça aide, car ça m'a aidé

1

essayez d’ajouter connection.close () à la fin. Je ne vois pas la libération de connexions dans votre code et leur renvoi explicite au pool de connexions. La connexion est donc renvoyée au pool de connexions uniquement au démarrage du CPG.

1
Sasa Ninkovic