TransactionScope eskaliert auf einigen Computern automatisch zu MSDTC?

284

In unserem Projekt verwenden wir TransactionScope, um sicherzustellen, dass unsere Datenzugriffsschicht ihre Aktionen in einer Transaktion ausführt. Wir möchten nicht, dass der MSDTC-Dienst auf den Computern unserer Endbenutzer aktiviert wird.

Das Problem ist, dass wir auf der Hälfte unserer Entwicklercomputer mit deaktiviertem MSDTC arbeiten können. Die andere Hälfte muss aktiviert sein, sonst wird die Fehlermeldung "MSDTC auf [SERVER] ist nicht verfügbar" angezeigt .

Ich habe mich wirklich am Kopf gekratzt und ernsthaft darüber nachgedacht, auf eine hausgemachte TransactionScope-ähnliche Lösung zurückzugreifen, die auf ADO.NET-Transaktionsobjekten basiert. Es ist scheinbar verrückt - der gleiche Code , das funktioniert (und nicht eskaliert) auf die Hälfte unseres Entwicklers tut eskalieren auf der anderen Entwickler.

Ich hatte auf eine bessere Antwort auf Trace gehofft, warum eine Transaktion zu DTC eskaliert, aber leider nicht.

Hier ist ein Beispielcode, der die Probleme verursacht. Auf den Computern, die versuchen zu eskalieren, versucht er, bei der zweiten Verbindung zu eskalieren. Open () (und ja, es ist derzeit keine andere Verbindung offen.)

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();
}

Wir haben uns wirklich eingegraben und versucht, das herauszufinden. Hier sind einige Informationen zu den Maschinen, auf denen es funktioniert:

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

Entwickler, bei denen es nicht funktioniert:

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

Ich sollte hinzufügen, dass alle Computer, um das Problem zu beheben, vollständig mit allem gepatcht wurden, was in Microsoft Update verfügbar ist.

Update 1:

Diese MSDN-Transaktionseskalationsseite gibt an, dass die folgenden Bedingungen dazu führen, dass eine Transaktion zu DTC eskaliert:

  1. Mindestens eine dauerhafte Ressource, die keine einphasigen Benachrichtigungen unterstützt, wird in die Transaktion aufgenommen.
  2. In die Transaktion werden mindestens zwei dauerhafte Ressourcen aufgenommen, die einphasige Benachrichtigungen unterstützen. Wenn Sie beispielsweise eine einzelne Verbindung mit registrieren, wird keine Transaktion hochgestuft. Wenn Sie jedoch eine zweite Verbindung zu einer Datenbank öffnen, wodurch die Datenbank registriert wird, erkennt die System.Transactions-Infrastruktur, dass es sich um die zweite dauerhafte Ressource in der Transaktion handelt, und eskaliert sie zu einer MSDTC-Transaktion.
  3. Eine Anforderung zum "Marshalling" der Transaktion in eine andere Anwendungsdomäne oder einen anderen Prozess wird aufgerufen. Zum Beispiel die Serialisierung des Transaktionsobjekts über eine Anwendungsdomänengrenze hinweg. Das Transaktionsobjekt wird nach Wert gemarshallt. Dies bedeutet, dass jeder Versuch, es über eine Anwendungsdomänengrenze (auch im selben Prozess) zu übergeben, zur Serialisierung des Transaktionsobjekts führt. Sie können die Transaktionsobjekte übergeben, indem Sie eine Remote-Methode aufrufen, die eine Transaktion als Parameter verwendet, oder Sie können versuchen, auf eine Remote-Transaktionskomponente zuzugreifen. Dies serialisiert das Transaktionsobjekt und führt zu einer Eskalation, wie wenn eine Transaktion über eine Anwendungsdomäne serialisiert wird. Es wird verteilt und der lokale Transaktionsmanager ist nicht mehr ausreichend.

Wir erleben nicht # 3. # 2 findet nicht statt, da immer nur eine Verbindung vorhanden ist und es sich auch um eine einzelne "dauerhafte Ressource" handelt. Gibt es eine Möglichkeit, dass # 1 passieren könnte? Eine SQL2005 / 8-Konfiguration, die dazu führt, dass einphasige Benachrichtigungen nicht unterstützt werden?

Update 2:

Persönlich wurden alle SQL Server-Versionen erneut untersucht - "Dev 3" hat tatsächlich SQL2008, und "Dev 4" ist tatsächlich SQL2005. Das wird mich lehren, meinen Mitarbeitern nie wieder zu vertrauen. ;) Aufgrund dieser Datenänderung bin ich mir ziemlich sicher, dass wir unser Problem gefunden haben. Unsere SQL2008-Entwickler hatten das Problem nicht, da SQL2008 eine Menge großartiger Funktionen enthält, die SQL2005 nicht hat.

Da wir SQL2005 unterstützen, können wir TransactionScope nicht mehr so ​​verwenden wie bisher. Wenn wir TransactionScope verwenden möchten, müssen wir ein einzelnes SqlConnection-Objekt übergeben ... Dies scheint in Situationen problematisch zu sein, in denen die SqlConnection nicht einfach weitergegeben werden kann. Es riecht nur nach einer globalen SqlConnection-Instanz. Bank!

Update 3

Nur um hier in der Frage zu klären:

SQL2008:

  • Ermöglicht mehrere Verbindungen innerhalb eines einzelnen TransactionScope (wie im obigen Beispielcode gezeigt).
  • Vorsichtsmaßnahme Nr. 1: Wenn diese mehreren SqlConnections verschachtelt sind, dh zwei oder mehr SqlConnections gleichzeitig geöffnet werden, wird TransactionScope sofort zu DTC eskaliert.
  • Vorsichtsmaßnahme Nr. 2: Wenn eine zusätzliche SqlConnection für eine andere 'dauerhafte Ressource' (dh einen anderen SQL Server) geöffnet wird, wird sie sofort zu DTC eskaliert

SQL2005:

  • Ermöglicht nicht mehrere Verbindungen innerhalb eines einzelnen TransactionScope-Zeitraums. Es eskaliert, wenn eine zweite SqlConnection geöffnet wird.

Update 4

Im Interesse dieser Frage machen noch ein Durcheinander nützlich, und nur aus Gründen mehr Klarheit, hier, wie Sie SQL2005 mit einem zu DTC eskalieren bekommen können einzelne SqlConnection :

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

Dies scheint mir nur kaputt zu sein, aber ich denke, ich kann verstehen, ob jeder Anruf SqlConnection.Open()aus dem Verbindungspool stammt.

"Warum könnte das aber passieren?" Wenn Sie einen SqlTableAdapter für diese Verbindung verwenden, bevor sie geöffnet wird, öffnet und schließt der SqlTableAdapter die Verbindung und beendet die Transaktion effektiv für Sie, da Sie sie jetzt nicht erneut öffnen können.

Um TransactionScope mit SQL2005 erfolgreich verwenden zu können, muss im Grunde ein globales Verbindungsobjekt vorhanden sein, das ab dem Zeitpunkt der ersten Instanziierung von TransactionScope geöffnet bleibt, bis es nicht mehr benötigt wird. Neben dem Code-Geruch eines globalen Verbindungsobjekts widerspricht das Öffnen und Schließen der Verbindung zuerst der Logik, eine Verbindung so spät wie möglich zu öffnen und so schnell wie möglich zu schließen.

Yoopergeek
quelle
Können Sie das Feld "Andere Dinge tun, die möglicherweise an der Umgebungstransaktion beteiligt sind oder nicht" erweitern? Sicherlich wirkt sich das, was sich dort befindet, stark auf das Verhalten des Codes aus?
RichardOD
2
"# 2 findet nicht statt, weil immer nur eine Verbindung gleichzeitig vorhanden ist" - # 2 besagt nicht, dass die zweite Verbindung zur gleichen Zeit geöffnet sein muss, sondern nur, dass sie in derselben Transaktion eingetragen werden muss.
Joe
3
Vielen Dank für die Rückmeldung mit Update 4, die zeigt, wie eine Eskalation mit nur einer einzigen SqlConnection erfolgen kann. Dies ist genau das, worauf ich gestoßen bin, obwohl ich sorgfältig darauf geachtet habe, dass nur eine einzige SqlConnection verwendet wird. Es ist schön zu wissen, dass der Computer verrückt ist und nicht ich. :-)
Oran Dennison
Wenn wir in Bezug auf das Verbindungspooling mehrere Verbindungen haben (und bei Bedarf verschachtelt sind), wenn wir jeweils eine öffnen und schließen, verwenden wir 1 echte Verbindungspoolressource oder 1 pro Verbindung, versuche ich dies zu rationalisieren, um festzustellen ob ich eine "enlistable" -Verbindung mit angemessenem Umfang haben soll (was ich vermeiden möchte)
brumScouse
1
Verschachtelte Verbindungen unter demselben Transaktionsbereich werden zu einer verteilten Transaktion hochgestuft. Ab SQL Server 2008 werden mehrere (nicht verschachtelte) Verbindungen unter demselben Transaktionsbereich nicht zu einer verteilten Transaktion befördert.
PreguntonCojoneroCabrón

Antworten:

71

SQL Server 2008 kann mehrere SQLConnections in einem verwenden, TransactionScopeohne eskalieren zu müssen, vorausgesetzt, die Verbindungen sind nicht gleichzeitig geöffnet, was zu mehreren "physischen" TCP-Verbindungen führen und daher eine Eskalation erfordern würde.

Ich sehe, dass einige Ihrer Entwickler SQL Server 2005 und andere SQL Server 2008 haben. Sind Sie sicher, dass Sie richtig identifiziert haben, welche eskalieren und welche nicht?

Die naheliegendste Erklärung wäre, dass Entwickler mit SQL Server 2008 nicht eskalieren.

Joe
quelle
Ja, die Details sind korrekt, und schaut sich tatsächlich jemand den Code an? Innerhalb des Transaktionsbereichs gibt es zwei Verbindungen. Es wird jedoch immer nur eine Verbindung zu einem bestimmten Zeitpunkt instanziiert und geöffnet. Nein, DTC wird auch nicht auf den Computern ausgeführt, die arbeiten.
Yoopergeek
1
"Es gibt jedoch immer nur eine Verbindung, die zu einem bestimmten Zeitpunkt instanziiert und geöffnet wird" - warum ist das relevant? Wenn Sie mit SQL2005 mehr als eine Verbindung innerhalb eines Transaktionsbereichs öffnen, eskalieren Sie, ob diese gleichzeitig geöffnet bleiben oder nicht. Was logisch ist, wenn Sie darüber nachdenken.
Joe
Sie und hwiechers lassen mich jetzt nachdenken, und ich bin gespannt darauf, am Montag mit der Arbeit zu beginnen und ihre einzelnen Computer genauer zu untersuchen und sicherzustellen, dass die SQL Server-Versionen den zuvor gemeldeten Versionen entsprechen.
Yoopergeek
19
Sie und hwiechers haben recht. Ich habe Ei auf meinem ganzen Gesicht. Danke, dass du mich mit dem Hinweisstab geschlagen hast. :) Weil du der Erste warst, bekommst du die Antwort. Ich möchte jedoch einen Punkt zur Klarstellung hinzufügen: Mit SQL2008 können mehrere Verbindungen geöffnet werden, jedoch nicht gleichzeitig. Es kann immer noch nur eine einzige Verbindung geöffnet sein, oder TransactionScope eskaliert zu DTC.
Yoopergeek
@Yoopergeek Ich konnte überprüfen, ob Ihr "nicht zur gleichen Zeit" wichtig ist, und die Antwort von @Joe entsprechend bearbeiten. Die Überwachung der TCP-Verbindungen während des Testens ergab, dass die alte TCP-Verbindung wiederverwendet wird, wenn Verbindungen nicht gleichzeitig verwendet werden. Daher TransactionScopekann dies mit einer einzelnen Verbindung COMMITauf der Serverseite auskommen , was eine Eskalation überflüssig machen würde.
Eugene Beresovsky
58

Das Ergebnis meiner Forschung zum Thema:

Geben Sie hier die Bildbeschreibung ein

Siehe Vermeiden Sie unerwünschte Eskalationen zu verteilten Transaktionen

Ich untersuche immer noch das Eskalationsverhalten von Oracle: Werden Transaktionen, die mehrere Verbindungen zu derselben Datenbank umfassen, zu DTC eskaliert?

Peter Meinl
quelle
1
Vielen Dank für Ihre Forschungsergebnisse. Es hat wirklich geholfen. Noch eine kurze Frage. Was ist der Unterschied zwischen TransactionScope () und sqlConnection.BeginTransaction ()?
Baig
Gemäß dieser Funktionsanforderung sollte sich ODAC 12C jetzt wie SQL 2008 verhalten und nicht mehr verteilt werden, wenn aufeinanderfolgende Verbindungen zu derselben Datenquelle verwendet werden.
Frédéric
31

Dieser Code führt bei der Verbindung zu 2005 zu einer Eskalation.

Überprüfen Sie die Dokumentation zu MSDN - http://msdn.microsoft.com/en-us/library/ms172070.aspx

Heraufstufbare Transaktionen in SQL Server 2008

In Version 2.0 von .NET Framework und SQL Server 2005 würde das Öffnen einer zweiten Verbindung in einem TransactionScope die Transaktion automatisch zu einer vollständig verteilten Transaktion hochstufen, selbst wenn beide Verbindungen identische Verbindungszeichenfolgen verwenden. In diesem Fall fügt eine verteilte Transaktion unnötigen Overhead hinzu, der die Leistung verringert.

Ab SQL Server 2008 und Version 3.5 von .NET Framework werden lokale Transaktionen nicht mehr zu verteilten Transaktionen heraufgestuft, wenn nach dem Schließen der vorherigen Transaktion eine andere Verbindung in der Transaktion geöffnet wird. Dies erfordert keine Änderungen an Ihrem Code, wenn Sie bereits das Verbindungspooling und die Registrierung in Transaktionen verwenden.

Ich kann nicht erklären, warum Dev 3: Windows 7 x64, SQL2005 erfolgreich ist und Dev 4: Windows 7 x64 fehlschlägt. Sind Sie sicher, dass das nicht umgekehrt ist?

hwiechers
quelle
10

Ich weiß nicht, warum diese Antwort gelöscht wurde, aber dies scheint einige relevante Informationen zu haben.

antwortete am 4. August 10 um 17:42 Eduardo

  1. Setzen Sie Enlist = false in der Verbindungszeichenfolge, um eine automatische Registrierung bei der Transaktion zu vermeiden.

  2. Tragen Sie die Verbindung manuell als Teilnehmer im Transaktionsbereich ein. [ Originalartikel veraltet] oder tun Sie dies: So verhindern Sie die automatische MSDTC-Promotion [archive.is]

Chris Marisic
quelle
msdn.microsoft.com/en-us/library/ms172153%28v=VS.80%29.aspx nicht gefunden, Visual Studio 2005 Dokumentation im Ruhestand
Kiquenet
2

Ich bin mir nicht sicher, ob eine verschachtelte Verbindung das Problem ist. Ich rufe eine lokale Instanz von SQL Server auf und es wird kein DTC generiert.

    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();
        }
    }
Iftikhar Ali
quelle
Welche Edition von SQL Server verwenden Sie? Ich frage mich, ob die Antwort von @Peter Meinl aktualisiert werden muss, um Änderungen in 2008R2 und / oder Denali widerzuspiegeln.
Yoopergeek
Ich verwende SQL Server 2008 R2.
Iftikhar Ali
Ich frage mich, ob 2008 R2 sich besser benimmt. Die Antwort von @hwiechers lässt mich auch fragen, ob die Version des Frameworks, gegen die Sie kompilieren, eine Eskalation verhindert. Schließlich frage ich mich, ob es einen Unterschied macht, eine lokale R2-Instanz zu sein. Ich wünschte, ich hätte die Zeit / Ressourcen, um zu untersuchen, wie sich dies mit der Veröffentlichung von 2008 R2 und SQL Server 2012
geändert hat
Sie sind sich nicht sicher, ob eine verschachtelte Verbindung das Problem ist? lol ... gut blühend, entferne es dann! Warum um alles in der Welt nisten Menschen mit Aussagen, wenn dies nicht unbedingt notwendig ist, werde ich nie erfahren.
Paul Zahra
1

TransactionScope eskaliert immer zur DTC-Transaktion, wenn Sie Zugriff auf mehr als eine Verbindung innerhalb verwenden. Der obige Code kann nur dann mit deaktiviertem DTC funktionieren, wenn Sie mit großer Wahrscheinlichkeit beide Male dieselbe Verbindung aus dem Verbindungspool erhalten.

"Das Problem ist, dass wir auf der Hälfte unserer Entwicklercomputer mit deaktiviertem MSDTC arbeiten können." Bist du sicher, dass es deaktiviert ist?

Amateur
quelle
0

Stellen Sie sicher, dass Ihr connectionString das Pooling nicht auf false setzt. Dies führt zu einer neuen Verbindung für jede neue SqlConnection im TransactionScope und eskaliert sie zu DTC.

FreddyV
quelle