2013-11-01 25 views
7

L'applicazione stessa descritta nel mio precedente question. Sul lato DAL io usoODP .NET si comporta in modo strano

Oracle.ManagedDataAccess, Version=4.121.1.0, Culture=neutral, PublicKeyToken=89b483f429c47342 

Ecco stringa di connessione:

User id=usr;Password=pwd;Data Source=database1;Validate connection=True;Connection timeout=8;Pooling=false 

Lo starange è che a volte ODP solleva fuori seguente eccezione:

Oracle.ManagedDataAccess.Client.OracleException (0xFFFFFC18): Connection request timed out 
    in OracleInternal.ConnectionPool.PoolManager`3.CreateNewPR(Int32 reqCount, Boolean bForPoolPopulation, ConnectionString csWithDiffOrNewPwd, String instanceName) 
    in OracleInternal.ConnectionPool.PoolManager`3.Get(ConnectionString csWithDiffOrNewPwd, Boolean bGetForApp, String affinityInstanceName, Boolean bForceMatch) 
    in OracleInternal.ConnectionPool.OraclePoolManager.Get(ConnectionString csWithNewPassword, Boolean bGetForApp, String affinityInstanceName, Boolean bForceMatch) 
    in OracleInternal.ConnectionPool.OracleConnectionDispenser`3.Get(ConnectionString cs, PM conPM, ConnectionString pmCS, SecureString securedPassword, SecureString securedProxyPassword) 
    in Oracle.ManagedDataAccess.Client.OracleConnection.Open() 
    in MySyncApp.DBRepository.GetChangedDataDB(DateTime startPeriod) in D:\MySyncApp\MySyncApp\DB.cs:line 23 
    in MySyncApp.Program.<>c__DisplayClass30.<>c__DisplayClass32.<Synchronize>b__2f(ID id) in D:\MySyncApp\MySyncApp\Program.cs:line 441 

Ma dopo questa eccezione, quando ho guarda le sessioni in Oracle, vedo che la connessione è attualmente in vita e contrassegnata come INACTIVE! Quindi, tali connessioni continueranno a rimanere sul lato server, esaurendo gradualmente il conteggio delle sessioni disponibili.

Non c'è niente di speciale nel mio codice, basta

public List<DataObj> GetChangedDataDB(DateTime startPeriod) 
{ 
    List<DataObj> list = new List<DataObj>(); 
    using (OracleConnection conn = new OracleConnection(this._connstr)) 
    { 
     conn.Open(); 

     using (OracleCommand comm = new OracleCommand("select data from table(usr.syncpackage.GetChanged(:pStart))", conn)) 
     { 
      comm.CommandTimeout = 10; 
      comm.Parameters.Add(":pStart", startPeriod); 

      using (OracleDataReader reader = comm.ExecuteReader()) 
      { 
       // ..omitted 
      } 
     } 

    } 
    return list; 
} 

Questo codice viene eseguito in Parallel.ForEach ciclo per tirare fuori i dati da un sacco di basi di dati simultaneamente. Possono anche esserci tre connessioni parallele allo stesso database (estrarre dati da diverse parti dello schema, ad esempio, da tre diversi dipartimenti di impresa).

L'Oracle è

Oracle Database 11g Enterprise Edition Release 11.2.0.3.0 - 64bit Produzione

Il processo di sincronizzazione si spara sul timer in intervalli di 10 secondi. Se c'è già runnning compito allora prossimo compito viene arrestato:

public static void Synchronize(object obj) 
    {    
     // ... omitted 
     log.Info("ITERATION_COMMON_START"); 

     if (Program.State == "Running") 
     { 
      log.Info("ITERATION_COMMON_END_BY_EXISTING"); 
      return; 
     } 

     lock (Program.StateLock) 
     { 
      Program.State = "Running"; 
     }     

     Parallel.ForEach(Global.config.dbs, new ParallelOptions { MaxDegreeOfParallelism = -1 }, (l) => 
     { 
      Console.WriteLine("Started synchronization for {0}", l.key); 
      DBRepository db = new DBRepository(l.connectionString); 

      Parallel.ForEach(l.departments, new ParallelOptions { MaxDegreeOfParallelism = -1 }, (department) => 
      {      
       DateTime ChangesFromTS = GetPreviousIterationTS; 
       List<DataObj> cdata = db.GetChangedDataDB(ChangesFromTS); 
       // ... doing the work here 
      } 

     } 

     // Finishing work 

     GC.Collect();    

     lock (Program.StateLock) 
     { 
      Program.State = ""; 
     } 

    } 

Ecco timer per invocare periodicamente sincronizzare le attività:

Program.getModifiedDataTimer = new Timer(Program.Synchronize, null, (int)Global.config.syncModifiedInterval * 1000, (int)Global.config.syncModifiedInterval * 1000); 

Global.config.syncModifiedInterval è espresso in secondi

ODP si comporta allo stesso modo quando accendo il pooling. Ha creato più connessioni di quelle consentite dalla direttiva Max pool size nelle stringhe di connessione con la stessa eccezione.

Per favore dimmi i tuoi pensieri e la tua esperienza su quella roba.


UPDATE

Ecco un pezzo di Oracle traccia quando viene sollevata un'eccezione:

(PUB) (ERR) OracleConnection.Open() (txnid=n/a) Oracle.ManagedDataAccess.Client.OracleException (0xFFFFFC18): Connection request timed out 
    in OracleInternal.ConnectionPool.PoolManager`3.CreateNewPR(Int32 reqCount, Boolean bForPoolPopulation, ConnectionString csWithDiffOrNewPwd, String instanceName) 
    in OracleInternal.ConnectionPool.PoolManager`3.Get(ConnectionString csWithDiffOrNewPwd, Boolean bGetForApp, String affinityInstanceName, Boolean bForceMatch) 
    in OracleInternal.ConnectionPool.OraclePoolManager.Get(ConnectionString csWithNewPassword, Boolean bGetForApp, String affinityInstanceName, Boolean bForceMatch) 
    in OracleInternal.ConnectionPool.OracleConnectionDispenser`3.Get(ConnectionString cs, PM conPM, ConnectionString pmCS, SecureString securedPassword, SecureString securedProxyPassword) 
    in Oracle.ManagedDataAccess.Client.OracleConnection.Open() 

UPDATE # 2

sembra che questo mostra le connessioni in alto b a causa della connessione ritardata, come la richiesta di stabilire una connessione oracle viene inviata ma la sua risposta viene ignorata. O i dati trasmessi a/da server sono corrotti mentre vanno a destinazione.

Connessioni continua a bloccarsi nell'elenco delle sessioni del server anche quando si spegne l'applicazione. Quando uccido una sessione, continua a rimanere in lista con l'etichetta "KILLED".


UPDATE # 3

Here è l'applicazione demo che rende stesso problema. Come ho detto in precedenza, appare su una cattiva connessione, è possibile simulare tale connessione utilizzando l'emulatore WANem. Here è lo stesso componente che utilizzo per la connettività del database. Spero per il tuo aiuto.

+0

È possibile fornire un dump dell'applicazione quando si verifica? – Olivier

+0

@Olivier Si prega di vedere il mio terzo aggiornamento di domande. Spero ti renda più facile riprodurre il problema. Ma sì, sicuramente, posso fare una discarica. – kseen

+0

Sei mai riuscito a risolverlo? –

risposta

4

Il timeout della connessione è estremamente basso, 8 secondi in generale e 10 secondi per i comandi che si stanno emettendo, provare ad aumentarlo fino a un minuto per vedere cosa succede. Non ho idea di quanto siano pesanti i tuoi algoritmi, ma è sufficiente che un thread da qualche parte fallisca con una query in meno di 8 secondi e otterrai questa eccezione.

Inoltre, non riesco a trovare la documentazione che indica che ODP.net non è thread-safe, ma non riesco a trovare alcuna documentazione dicendo che è uno, quindi se non altro aiuta a provare questo cambiamento:

Parallel.ForEach(Global.config.dbs, new ParallelOptions { MaxDegreeOfParallelism = -1 }, (l) => 
    { 
     Console.WriteLine("Started synchronization for {0}", l.key); 

     Parallel.ForEach(l.departments, new ParallelOptions { MaxDegreeOfParallelism = -1 }, (department) => 
     {      
      // Now local to the executing thread. 
      DBRepository db = new DBRepository(l.connectionString); 

      DateTime ChangesFromTS = GetPreviousIterationTS; 
      List<DataObj> cdata = db.GetChangedDataDB(ChangesFromTS); 
      // ... doing the work here 
     } 

    } 
0

avete fatto prova ad incorrere nel timeout del comando (riga di codice nel metodo GetChangedDataDB)? Qualcosa di simile

comm.CommandTimeout = 360; 

Ho creato progetto che gestiscono un enorme di dati, e ho anche ottenuto il messaggio di errore come lei, così ho aggiungere sempre più valore CommandTimeout, allora funziona, ma non sono sicuro se si incontrato lo stesso caso come me.

In altro modo, ho visto hai portata Parallel.ForEach portata Parallel.ForEach, e penso che si può tentare di modificare il secondo campo di applicazione con l'utilizzo di Task Parallel Library. si potrebbe imparare più qui http://msdn.microsoft.com/en-us/library/dd537609.aspx

Poi il secondo ambito Parallel.ForEach dovrebbe essere come questo

Task task = new Task(() => 
Parallel.ForEach(l.departments, new ParallelOptions { MaxDegreeOfParallelism = -1 }, (department) => 
{ 
    // Now local to the executing thread. 
    DBRepository db = new DBRepository(l.connectionString); 

    DateTime ChangesFromTS = GetPreviousIterationTS; 
    List<DataObj> cdata = db.GetChangedDataDB(ChangesFromTS); 
    // ... doing the work here 
} 
)); //close lambda expression 

task.Start(); 
1

Ho avuto lo stesso problema con il mio codice F #. Genera contemporaneamente molte connessioni e non utilizza la Libreria parallela attività. Sono d'accordo con flindeberg, Motomoto Pink e la tua conclusione che il problema è che la connessione confermata in risposta da Oracle viene dopo l'eccezione di timeout della richiesta di connessione.

Come altri, suggerisco di aumentare il timeout della richiesta di connessione. Tuttavia, è possibile considerare l'utilizzo del pool di connessioni con il parametro Min Pool Size impostato sul numero di thread paralleli generati che apriranno le connessioni. L'utilizzo del pool di connessioni potrebbe aumentare notevolmente le prestazioni sia lato client che lato server quando il numero di reparti è elevato.

Problemi correlati