TransactionScope passa automaticamente a MSDTC su alcune macchine?


284

Nel nostro progetto stiamo utilizzando TransactionScope per garantire che il nostro livello di accesso ai dati esegua le sue azioni in una transazione. Puntiamo a non richiedere l'abilitazione del servizio MSDTC sui computer dei nostri utenti finali.

Il problema è che su metà delle macchine dei nostri sviluppatori, possiamo eseguire MSDTC disabilitato. L'altra metà deve averlo abilitato o ottengono il messaggio di errore "MSDTC su [SERVER] non disponibile" .

Mi ha davvero fatto grattare la testa e mi sta seriamente prendendo in considerazione l'idea di tornare a una soluzione simile a TransactionScope fatta in casa basata su oggetti di transazione ADO.NET. E 'apparentemente folle - lo stesso codice che funziona (e non si intensificano) sulla metà del nostro sviluppatore fa escalation d'altro sviluppatore.

Speravo in una risposta migliore a Trace perché una transazione è stata inoltrata a DTC, ma sfortunatamente non lo è.

Ecco un po 'di codice di esempio che causerà il problema, sui computer che tentano di eseguire l'escalation, tenta di eseguire l'escalation sulla seconda connessione.Open () (e sì, al momento non esiste un'altra connessione aperta).

using (TransactionScope transactionScope = new TransactionScope() {
   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();
         using (SqlDataReader reader = command.ExecuteReader()) {
            // use the reader
            connection.Close();
         }
      }
   }

   // Do other stuff here that may or may not involve enlisting 
   // in the ambient transaction

   using (SqlConnection connection = new SqlConnection(_ConStr)) {
      using (SqlCommand command = connection.CreateCommand()) {
         // prep the command
         connection.Open();  // Throws "MSDTC on [SERVER] is unavailable" on some...

         // gets here on only half of the developer machines.
      }
      connection.Close();
   }

   transactionScope.Complete();
}

Abbiamo davvero scavato e cercato di capirlo. Ecco alcune informazioni sulle macchine su cui funziona:

  • Dev 1: Windows 7 x64 SQL2008
  • Dev 2: Windows 7 x86 SQL2008
  • Dev 3: Windows 7 x64 SQL2005 SQL2008

Sviluppatori su cui non funziona:

  • Dev 4: Windows 7 x64, SQL2008 SQL2005
  • Dev 5: Windows Vista x86, SQL2005
  • Dev 6: Windows XP X86, SQL2005
  • My Home PC: Windows Vista Home Premium, x86, SQL2005

Dovrei aggiungere che tutte le macchine, nel tentativo di dare la caccia al problema, sono state completamente patchate con tutto ciò che è disponibile da Microsoft Update.

Aggiornamento 1:

La pagina di escalation delle transazioni MSDN indica che le seguenti condizioni causeranno l'escalation di una transazione a DTC:

  1. Almeno una risorsa durevole che non supporta le notifiche monofase è inclusa nella transazione.
  2. Nella transazione sono incluse almeno due risorse durevoli che supportano le notifiche monofase. Ad esempio, l'inserimento di una singola connessione con non comporta la promozione di una transazione. Tuttavia, ogni volta che si apre una seconda connessione a un database che causa l'arruolamento del database, l'infrastruttura System.Transactions rileva che è la seconda risorsa duratura nella transazione e la converte in una transazione MSDTC.
  3. Viene invocata una richiesta per "eseguire il marshalling" della transazione in un dominio applicazione diverso o in un processo diverso. Ad esempio, la serializzazione dell'oggetto transazione attraverso un limite del dominio dell'applicazione. L'oggetto transazione è suddiviso per valore, il che significa che qualsiasi tentativo di passarlo attraverso un confine del dominio dell'applicazione (anche nello stesso processo) comporta la serializzazione dell'oggetto transazione. È possibile passare gli oggetti della transazione effettuando una chiamata su un metodo remoto che accetta una Transazione come parametro oppure si può provare ad accedere a un componente remoto gestito dalla transazione. Ciò serializza l'oggetto della transazione e si traduce in un'escalation, come quando una transazione è serializzata in un dominio dell'applicazione. Viene distribuito e il gestore delle transazioni locale non è più adeguato.

Non stiamo riscontrando il n. 3. # 2 non sta accadendo perché esiste sempre una sola connessione alla volta ed è anche una singola "risorsa durevole". C'è un modo in cui potrebbe accadere il n. 1? Qualche configurazione SQL2005 / 8 che non supporta le notifiche monofase?

Aggiornamento 2:

Ho riesaminato, personalmente, le versioni di SQL Server di tutti: "Dev 3" in realtà ha SQL2008 e "Dev 4" è in realtà SQL2005. Questo mi insegnerà a non fidarmi mai più dei miei colleghi. ;) A causa di questo cambiamento nei dati, sono abbastanza sicuro di aver trovato il nostro problema. I nostri sviluppatori SQL2008 non stavano riscontrando il problema perché SQL2008 ha copiose quantità di fantastici inclusi che SQL2005 non ha.

Mi dice anche che, poiché supporteremo SQL2005, non possiamo usare TransactionScope come siamo stati e se vogliamo usare TransactionScope dovremo passare un singolo oggetto SqlConnection in giro ... che sembra problematico in situazioni in cui SqlConnection non può essere facilmente trasmesso ... ha solo l'odore dell'istanza global-SqlConnection. Banco!

Aggiornamento 3

Giusto per chiarire qui nella domanda:

SQL2008:

  • Consente connessioni multiple all'interno di un singolo TransactionScope (come dimostrato nel codice di esempio sopra).
  • Avvertenza n. 1: se si annidano quelle più connessioni SqlConnessione, ovvero due o più connessioni SqlConness vengono aperte contemporaneamente, TransactionScope passerà immediatamente al DTC.
  • Avvertenza n. 2: se un'ulteriore SqlConnection viene aperta a una diversa "risorsa durevole" (ovvero: un diverso SQL Server), passerà immediatamente al DTC

SQL2005:

  • Non consente più connessioni all'interno di un singolo TransactionScope, punto. Si intensificherà quando / se viene aperto un secondo SqlConnection.

Aggiornamento 4

Nell'interesse di fare questa domanda ancora più di un pasticcio utile, e solo per amor di più di chiarezza, ecco come si può arrivare SQL2005 a crescere a DTC con un singolo SqlConnection :

using (TransactionScope transactionScope = new TransactionScope()) {
   using (SqlConnection connection = new SqlConnection(connectionString)) {
      connection.Open();
      connection.Close();
      connection.Open(); // escalates to DTC
   }
}

Questo mi sembra rotto, ma credo di poter capire se ogni chiamata a SqlConnection.Open()viene catturata dal pool di connessioni.

"Perché potrebbe succedere, però?" Bene, se usi un SqlTableAdapter contro quella connessione prima che venga aperto, SqlTableAdapter aprirà e chiuderà la connessione, completando in modo efficace la transazione per te perché ora non puoi riaprirla.

Quindi, fondamentalmente, per utilizzare con successo TransactionScope con SQL2005 è necessario disporre di una sorta di oggetto di connessione globale che rimanga aperto dal punto in cui TransactionScope viene istanziato fino a quando non è più necessario. Oltre all'odore di codice di un oggetto connessione globale, aprire prima la connessione e chiuderla per ultima è in contrasto con la logica di aprire una connessione il più tardi possibile e chiuderla il prima possibile.


Puoi espandere "Fai qui altre cose che potrebbero o meno coinvolgere nella transazione ambientale". Sicuramente ciò che è presente influenza notevolmente il comportamento del codice?
RichardOD,

2
"Il n. 2 non sta accadendo perché esiste sempre una sola connessione alla volta" - Il n. 2 non dice che la seconda connessione deve essere aperta contemporaneamente, solo che deve essere inclusa nella stessa transazione.
Joe,

3
Grazie mille per aver segnalato con l'aggiornamento 4 che mostra come può verificarsi l'escalation con un solo SqlConnection. Questo è esattamente quello che stavo incontrando, nonostante mi assicurassi attentamente che fosse usato solo un singolo SqlConnection. È bello sapere che è il computer che è pazzo e non io. :-)
Oran Dennison,

In termini di pool di connessioni, se abbiamo più connessioni (e nidificate se necessario) se ne apriamo e chiudiamo una alla volta, stiamo utilizzando 1 risorsa pool di connessione reale o 1 per connessione, sto cercando di razionalizzare questo per determinare se avere o meno una riunione "arruolabile" adeguatamente mirata (che vorrei evitare)
brumScouse

1
Le connessioni nidificate nell'ambito dello stesso ambito di transazione promuoveranno una transazione distribuita. Da SQL Server 2008 e oltre le connessioni multiple (non annidate) nello stesso ambito di transazione non promuoveranno un transaciton distribuito.
PreguntonCojoneroCabrón

Risposte:


71

SQL Server 2008 può utilizzare più messaggi SQLConnectionin uno TransactionScopesenza eseguire l' escalation, a condizione che le connessioni non siano aperte contemporaneamente, il che comporterebbe più connessioni TCP "fisiche" e richiederebbe quindi l'escalation.

Vedo che alcuni dei tuoi sviluppatori hanno SQL Server 2005 e altri SQL Server 2008. Sei sicuro di aver identificato correttamente quali stanno aumentando e quali no?

La spiegazione più ovvia sarebbe che gli sviluppatori con SQL Server 2008 sono quelli che non stanno aumentando.


Sì, i dettagli sono corretti e qualcuno sta davvero guardando il codice? Esistono due connessioni nell'ambito della transazione, tuttavia esiste sempre una sola connessione istanziata e aperta in un singolo momento. Inoltre, no, DTC non è in esecuzione sui computer che funzionano.
Yoopergeek,

1
"tuttavia, esiste solo una connessione istanziata e aperta in un solo momento nel tempo" - perché è rilevante? Con SQL2005, se si apre più di una connessione nell'ambito di una transazione, si intensificherà se rimangono aperte contemporaneamente. Il che è logico se ci pensi.
Joe,

Tu e Hwiecher ora avete una seconda ipotesi e sono ansioso di mettermi al lavoro lunedì e ispezionare più da vicino le loro singole macchine e assicurarsi che le versioni di SQL Server siano come riportato in precedenza.
Yoopergeek,

19
Tu e Hwiecher avete ragione. Ho l'uovo su tutta la faccia. Grazie per avermi colpito con la chiave. :) Perché tu eri il primo, ottieni la risposta. Vorrei aggiungere un punto di chiarimento, tuttavia: SQL2008 consente di aprire più connessioni, ma non allo stesso tempo. È comunque possibile aprire una sola connessione alla volta o TransactionScope passerà a DTC.
Yoopergeek,

@Yoopergeek Ho potuto verificare che il tuo "non allo stesso tempo" è importante e modificato la risposta di @Joe di conseguenza. Il monitoraggio delle connessioni TCP durante il test ha mostrato che la vecchia connessione TCP verrà riutilizzata quando le connessioni non vengono utilizzate contemporaneamente e quindi è TransactionScopepossibile accontentarsi di una singola COMMITsul lato server, il che renderebbe superflua l'escalation.
Eugene Beresovsky,

58

Il risultato della mia ricerca sull'argomento:

inserisci qui la descrizione dell'immagine

Vedere Evitare l'escalation indesiderata alle transazioni distribuite

Sto ancora studiando il comportamento di escalation di Oracle: le transazioni che coprono più connessioni allo stesso DB vengono convertite in DTC?


1
Grazie per aver condiviso la tua ricerca. Mi ha davvero aiutato. Un'altra domanda veloce. Qual è la differenza tra TransactionScope () e sqlConnection.BeginTransaction ()?
Baig

Secondo questa richiesta di funzionalità , ODAC 12C dovrebbe ora comportarsi come SQL 2008, non promuovendo la distribuzione quando si utilizzano connessioni consecutive alla stessa origine dati.
Frédéric,

31

Tale codice sarà causare un'escalation quando ci si collega al 2005.

Controlla la documentazione su MSDN - http://msdn.microsoft.com/en-us/library/ms172070.aspx

Transazioni promuovibili in SQL Server 2008

Nella versione 2.0 di .NET Framework e SQL Server 2005, l'apertura di una seconda connessione all'interno di TransactionScope promuove automaticamente la transazione in una transazione distribuita completa, anche se entrambe le connessioni utilizzano stringhe di connessione identiche. In questo caso, una transazione distribuita aggiunge un sovraccarico non necessario che riduce le prestazioni.

A partire da SQL Server 2008 e versione 3.5 di .NET Framework, le transazioni locali non vengono più promosse a transazioni distribuite se viene aperta un'altra connessione nella transazione dopo la chiusura della transazione precedente. Ciò non richiede modifiche al codice se si sta già utilizzando il pool di connessioni e l'arruolamento nelle transazioni.

Non riesco a spiegare perché Dev 3: Windows 7 x64, SQL2005 ha esito positivo e Dev 4: Windows 7 x64 ha esito negativo. Sei sicuro che non sia il contrario?


10

Non so perché questa risposta sia stata eliminata, ma sembra che abbia alcune informazioni pertinenti.

rispose il 4 agosto 10 alle 17:42 Eduardo

  1. Impostare Enlist = false sulla stringa di connessione per evitare l'arruolamento automatico sulla transazione.

  2. Elenca manualmente la connessione come partecipanti nell'ambito della transazione. [ articolo originale obsoleto] o come fare: Come prevenire la promozione automatica MSDTC [archive.is]


msdn.microsoft.com/en-us/library/ms172153%28v=VS.80%29.aspx non trovato, Visual Studio 2005 Documentazione ritirata
Kiquenet

2

Non sono troppo sicuro se il problema è la connessione nidificata. Sto chiamando un'istanza locale di SQL Server e non genera il DTC ??

    public void DoWork2()
    {
        using (TransactionScope ts2 = new TransactionScope())
        {
            using (SqlConnection conn1 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;"))
            {
                SqlCommand cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                cmd.Connection = conn1;
                cmd.Connection.Open();
                cmd.ExecuteNonQuery();

                using (SqlConnection conn2 = new SqlConnection("Data Source=Iftikhar-PC;Initial Catalog=LogDB;Integrated Security=SSPI;Connection Timeout=100"))
                {
                    cmd = new SqlCommand("Insert into Log values(newid(),'" + "Dowork2()" + "','Info',getDate())");
                    cmd.Connection = conn2;
                    cmd.Connection.Open();
                    cmd.ExecuteNonQuery();
                }
            }

            ts2.Complete();
        }
    }

Quale edizione di SQL Server stai utilizzando? Mi chiedo se la risposta di @Peter Meinl debba essere aggiornata per riflettere eventuali modifiche apportate nel 2008R2 e / o Denali.
Yoopergeek,

Sto usando SQL Server 2008 R2.
Iftikhar Ali,

Mi chiedo se il 2008 R2 abbia un comportamento migliore? La risposta di @hwiechers mi fa anche chiedere se la versione del Framework contro cui stai compilando impedisce l'escalation. Infine, mi chiedo se essere un'istanza R2 locale faccia la differenza. Vorrei avere il tempo / le risorse per indagare su come è cambiato con la versione 2008 R2 e SQL Server 2012.
Yoopergeek,

Non sei sicuro che il problema sia la connessione nidificata? lol ... ben fiorente rimuovilo allora !, perché mai le persone nidificano usando le dichiarazioni quando non sono assolutamente necessarie, non lo saprò mai.
Paul Zahra,

1

TransactionScope esegue sempre l'escalation alla transazione DTC, se si utilizza l'accesso a più di 1 connessione all'interno. L'unico modo in cui il codice sopra riportato può funzionare con DTC disabilitato è se, per un'enorme possibilità, si ottiene la stessa connessione dal pool di connessioni entrambe le volte.

"Il problema è che su metà delle macchine dei nostri sviluppatori, possiamo eseguire MSDTC disabilitato". Sei sicuro che sia disabilitato;)


0

Assicurarsi che connectionString non imposti il ​​pool su false. Ciò comporterà una nuova connessione per ogni nuova connessione Sql in TransactionScope e la convertirà in DTC.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.