2010-08-03 5 views
7

La nostra applicazione (che utilizza NHibernate e ASP.NET MVC), quando sottoposta a stress test genera molti errori di transazione NHibernate. I tipi principali sono:Errori di transazione NHibernate

  1. transazione non collegata, o è stato disconnesso
  2. riga è stata aggiornata o eliminata da un'altra transazione (o unsaved-value mappatura non era corretto)
  3. transazione (ID di processo 177) critico su bloccare le risorse con un altro processo ed è stato scelto come vittima del deadlock. Rieseguire la transazione.

Qualcuno può aiutarmi a identificare il motivo dell'eccezione 1? So che devo gestire le altre eccezioni nel mio codice. Qualcuno può indicarmi risorse che possano aiutarmi a gestire questi errori in modo efficiente?

Q. Come gestiamo sessioni e transazioni?

A. Stiamo utilizzando Autofac. Per ogni richiesta del server, creiamo un nuovo contenitore di richieste che ha la sessione nell'ambito di validità del contenitore. All'attivazione della sessione iniziamo la transazione. Al termine della richiesta, effettuiamo il commit della transazione. In alcuni casi, la transazione può essere enorme. Per semplificare, ogni richiesta del server è contenuta in una transazione.

+0

Come stai gestendo le sessioni e le transazioni? –

+0

hai mai trovato una soluzione a questo? –

risposta

1

Date un'occhiata a questa discussione: http://n2cms.codeplex.com/Thread/View.aspx?ThreadId=85016

Fondamentalmente ciò che dice come una possibile causa di questa eccezione:

2010-02-17 21: 01: 41,204 1 WARN NHibernate.Util.ADOExceptio nReporter - System.Data.SqlClient.SqlException: Il log delle transazioni per il database 'database' è pieno. Per scoprire perché lo spazio nel log non può essere riutilizzato, vedere la colonna log_reuse_wait_desc in sys.databases

Come la dimensione del log delle transazioni è proporzionale alla quantità di lavoro svolto durante la transazione, forse si dovrebbe cerca di mettere i tuoi confini transazionali attraverso la "gestione" dei comandi da parte dei gestori sulla parte di scrittura delle transazioni. Quindi, con una sessione # X, carica lo stato che desideri modificare, lo metti in mutamento e lo commetti, tutto come un'unica unità di lavoro in #X.

Per quanto riguarda il lato di lettura delle cose, si potrebbe quindi avere un'altra IS # Y che legge i dati; questa ISession potrebbe essere utilizzata per le letture di batch all'interno ad es. Segnaposto ripetibile o qualcosa di simile con la funzione Futures e potrebbe semplicemente essere letto da una cache (anche se in realtà è una stampella). Facendolo in questo modo potrebbe aiutarti a recuperare da "errori" che non lo sono; livelock, deadlock e transazioni delle vittime.

Il problema con l'utilizzo di una transazione per richiesta è che l'ISession acquisisce molti dati di conservazione dei libri mentre si lavora, il che è parte integrante della transazione.Quindi il database segna i dati (rols, cols, tabelle, ecc.) Come partecipi alla transazione, facendo sì che il grafo di attesa si estenda su "entità" (nel senso del database, non nel senso DDD), che non sono in realtà parte del limite transazionale del comando richiesto dall'applicazione.

Per la cronaca (altre persone che utilizzano questo comando), Fabio aveva a post che si occupava di gestire le eccezioni dal livello dati. Citando un po 'del suo codice;

public class MsSqlExceptionConverterExample : ISQLExceptionConverter 
{ 
    public Exception Convert(AdoExceptionContextInfo exInfo) 
    { 
     var sqle = ADOExceptionHelper.ExtractDbException(exInfo.SqlException) as SqlException; 
     if(sqle != null) 
     { 
      switch (sqle.Number) 
      { 
       case 547: 
        return new ConstraintViolationException(exInfo.Message, 
         sqle.InnerException, exInfo.Sql, null); 
       case 208: 
        return new SQLGrammarException(exInfo.Message, 
         sqle.InnerException, exInfo.Sql); 
       case 3960: 
        return new StaleObjectStateException(exInfo.EntityName, exInfo.EntityId); 
      } 
     } 
     return SQLStateConverter.HandledNonSpecificException(exInfo.SqlException, 
      exInfo.Message, exInfo.Sql); 
    } 
} 
  • 547 è il numero di eccezione per conflitto di vincoli.
  • 208 è il numero di eccezione per un nome oggetto non valido nell'SQL.
  • 3960 è il numero di eccezione per la transazione di isolamento dello snapshot interrotta a causa di conflitto di aggiornamento.

Quindi, se si verificano problemi di concorrenza come quello che descrivi; ricorda che invalideranno la tua ISession e che dovrai gestirli come sopra.

Parte di ciò che si sta cercando è CQRS, in cui si hanno lati di lettura e scrittura separati. Questo potrebbe aiutare: http://abdullin.com/cqrs/, http://cqrsinfo.com.

Quindi per riassumere; i tuoi problemi potrebbero essere legati al modo in cui gestisci le tue transazioni. Inoltre, prova a eseguire select log_wait_reuse_desc from sys.databases where name='MyDBName' e vedere cosa ti dà.

1

questa discussione c'è una spiegazione: http://groups.google.com/group/nhusers/browse_thread/thread/7f5fb68a00829d13

In breve, il database probabilmente ripristina la transazione di per sé causa di qualche errore, in modo che quando si tenta di eseguire il rollback la transazione successiva è già rollback e in uno stato zombie. Questo tende a nascondere il vero motivo del rollback poiché tutto ciò che vedi è una TransactionException invece dell'eccezione che ha effettivamente innescato il rollback in primo luogo.

Non penso che ci sia molto che si possa fare al di là della registrazione e cercando di capire cosa stia causando l'errore sottostante.

0

So che questo post è stato un po 'indietro e presumo che tu l'abbia corretto, ma sembra che tu abbia problemi di condivisione dei thread con l'NHibernate ISession che non è protetto da thread. Fondamentalmente 1 thread sta avviando una transazione e un altro sta tentando di chiuderlo causando tutti i tipi di caos.