web-dev-qa-db-fra.com

TransactionScope passant automatiquement au MSDTC sur certaines machines?

Dans notre projet, nous utilisons TransactionScope pour nous assurer que notre couche d'accès aux données exécute ses actions dans une transaction. Nous visons pas exiger que le service MSDTC soit activé sur les ordinateurs de nos utilisateurs finaux.

Le problème, c'est que sur la moitié de nos machines de développement, nous pouvons fonctionner avec MSDTC désactivé. L’autre moitié doit l’activer, sinon le message d’erreur "MSDTC sur [SERVEUR] est indisponible".

Cela m'a vraiment gratté la tête et m'a sérieusement envisagé de revenir à une solution maison de type TransactionScope basée sur des objets de transaction ADO.NET. C'est apparemment fou - le même code qui fonctionne (et n'escalade pas) sur la moitié de notre développeur ne escalade sur l'autre développeur.

J'espérais une meilleure réponse à trace pourquoi une transaction est transférée au format DTC mais malheureusement, ce n'est pas le cas.

Voici un exemple de code qui posera problème, sur les machines qui tentent d’escalader, il essaie d’escalader sur la deuxième connexion.Open () (et oui, aucune autre connexion n’est ouverte à ce moment.)

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

Nous avons vraiment creusé et essayé de comprendre cela. Voici quelques informations sur les machines sur lesquelles cela fonctionne:

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

Développeurs cela ne fonctionne pas sur:

  • Dev 4: Windows 7 x64, SQL2008  SQL2005
  • Dev 5: Windows Vista x86, SQL2005
  • Dev 6: Windows XP X86, SQL2005
  • Mon ordinateur personnel: Windows Vista Édition Familiale Premium, x86, SQL2005

Je devrais ajouter que toutes les machines, dans le but de résoudre le problème, ont été entièrement corrigées avec tout ce qui est disponible dans Microsoft Update.

Mise à jour 1:

Cette page d'escalade de transaction MSDN indique que les conditions suivantes entraîneront l'escalade d'une transaction en DTC:

  1. Au moins une ressource durable qui ne prend pas en charge les notifications en une seule phase est inscrite dans la transaction.
  2. Au moins deux ressources durables prenant en charge les notifications en une phase sont inscrites dans la transaction. Par exemple, l’inscription d’une connexion unique avec ne provoque pas la promotion d’une transaction. Toutefois, chaque fois que vous ouvrez une deuxième connexion à une base de données entraînant l'inscription de la base de données, l'infrastructure System.Transactions détecte qu'il s'agit de la deuxième ressource durable de la transaction et la transfère à une transaction MSDTC.
  3. Une demande de "marshaler" la transaction vers un domaine d'application différent ou un processus différent est invoquée. Par exemple, la sérialisation de l'objet de transaction à travers une limite de domaine d'application. L'objet de transaction est marshalé par valeur, ce qui signifie que toute tentative de le faire traverser une limite de domaine d'application (même dans le même processus) entraîne la sérialisation de l'objet de transaction. Vous pouvez transmettre les objets de transaction en effectuant un appel sur une méthode distante prenant une transaction en tant que paramètre ou vous pouvez essayer d'accéder à un composant desservi par des transactions distantes. Cela sérialise l'objet de transaction et entraîne une escalade, comme lorsqu'une transaction est sérialisée dans un domaine d'application. Il est en cours de distribution et le gestionnaire de transactions local n'est plus adéquat.

Nous ne faisons pas l'expérience # 3. Le numéro 2 ne se produit pas car il n’ya jamais qu’une connexion à la fois, et qu’il s’agit également d’une ressource unique. Est-il possible que le n ° 1 se produise? Une configuration SQL2005/8 qui empêche les notifications monophasées?

Mise à jour 2:

Nouvelle enquête sur les versions SQL Server de tout le monde - "Dev 3" a en fait SQL2008 et "Dev 4" est en fait SQL2005. Cela m'apprendra à ne plus jamais faire confiance à mes collègues. ;) En raison de ce changement dans les données, je suis à peu près sûr que nous avons trouvé notre problème. Les développeurs de SQL2008 ne rencontraient pas le problème, car SQL2008 renferme une quantité impressionnante de ressources impressionnantes que SQL2005 n’a pas.

Cela me dit également que parce que nous allons prendre en charge SQL2005, nous ne pouvons pas utiliser TransactionScope comme nous l’avons été, et si nous voulons utiliser TransactionScope, nous devrons faire passer un seul objet SqlConnection ... ce qui semble problématique dans les cas où SqlConnection ne peut pas être facilement transmis ... ça sent juste l'instance de global-SqlConnection. Banc!

Mise à jour 3

Juste pour clarifier ici dans la question:

SQL2008:

  • Autorise plusieurs connexions dans un seul TransactionScope (comme illustré dans l'exemple de code ci-dessus.)
  • Mise en garde n ° 1: si ces connexions SqlConnections multiples sont imbriquées, c'est-à-dire si deux connexions SqlConnexions ou plus sont ouvertes en même temps, TransactionScope passera immédiatement au format DTC.
  • Mise en garde n ° 2: si une connexion SqlConnection supplémentaire est ouverte vers un autre 'ressource durable' (c'est-à-dire: un serveur SQL différent,), il passera immédiatement au DTC

SQL2005:

  • Ne permet pas plusieurs connexions dans un seul TransactionScope, période. Il s’aggravera si/si un deuxième SqlConnection est ouvert.

Mise à jour 4

Dans l’intérêt de rendre cette question encore plus d'un désordre utile, et pour plus de clarté, voici comment vous pouvez faire en sorte que SQL2005 passe au DTC avec un singleSqlConnection:

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

Cela me semble cassé, mais je suppose que je peux comprendre si chaque appel à SqlConnection.Open() est en cours de saisie dans le pool de connexions.

"Pourquoi cela pourrait-il arriver, cependant?" Eh bien, si vous utilisez un SqlTableAdapter contre cette connexion avant qu'elle ne soit ouverte, le SqlTableAdapter ouvrira et fermera la connexion, terminant ainsi la transaction pour vous car vous le faites maintenant. ne peut pas le rouvrir.

En résumé, pour pouvoir utiliser avec succès TransactionScope avec SQL2005, vous devez disposer d’une sorte d’objet de connexion globale qui reste ouvert à partir du moment où le premier TransactionScope est instancié jusqu’à ce qu’il ne soit plus nécessaire. En plus de l'odeur de code d'un objet de connexion global, ouvrir la connexion en premier et la fermer en dernier est contraire à la logique d'ouvrir une connexion le plus tard possible et de la fermer dès que possible.

279
Yoopergeek

SQL Server 2008 peut utiliser plusieurs SQLConnections dans un TransactionScope sans escalade, à condition que les connexions ne soient pas ouvertes en même temps, ce qui entraînerait plusieurs connexions "physiques" TCP. et nécessitent donc une escalade.

Je vois que certains de vos développeurs utilisent SQL Server 2005 et d’autres SQL Server 2008. Etes-vous sûr d’avoir correctement identifié ceux qui montent et ceux qui ne le sont pas?

L'explication la plus évidente serait que les développeurs avec SQL Server 2008 sont ceux qui n'escaladent pas.

71
Joe

Le résultat de mes recherches sur le sujet:

enter image description here

Voir éviter l'escalade indésirable en transactions distribuées

J'enquête toujours sur le comportement d'escalade d'Oracle: Les transactions couvrant plusieurs connexions à la même base de données sont-elles converties en DTC?

58
Peter Meinl

Ce code will provoque une escalade lors de la connexion à 2005.

Consultez la documentation sur MSDN - http://msdn.Microsoft.com/en-us/library/ms172070.aspx

Transactions promotionnelles dans SQL Server 2008

Dans la version 2.0 de .NET Framework et SQL Server 2005, l'ouverture d'une deuxième connexion à l'intérieur d'un TransactionScope favoriserait automatiquement la transaction en transaction distribuée complète, même si les deux connexions utilisaient des chaînes de connexion identiques. Dans ce cas, une transaction distribuée ajoute une surcharge inutile qui diminue les performances.

À partir de SQL Server 2008 et de la version 3.5 du .NET Framework, les transactions locales ne sont plus promues en transactions distribuées si une autre connexion est ouverte dans la transaction après la fermeture de la transaction précédente. Cela ne nécessite aucune modification de votre code si vous utilisez déjà le regroupement de connexions et l'inscription à des transactions.

Je ne peux pas expliquer pourquoi Dev 3: Windows 7 x64, SQL2005 réussit et Dev 4: Windows 7 x64 échoue. Êtes-vous sûr que ce n'est pas l'inverse?

31
hwiechers

Je ne sais pas pourquoi cette réponse a été supprimée, mais cela semble contenir des informations pertinentes.

répondu 4 août '10 à 17h42 Eduardo

  1. Définissez Enlist = false sur la chaîne de connexion pour éviter l'inscription automatique lors d'une transaction.

  2. Inscrire manuellement la connexion en tant que participants dans la portée de la transaction. [ article original obsolète] ou procédez comme suit: Comment empêcher la promotion automatique du MSDTC[archive.is]

10
Chris Marisic

Je ne sais pas trop si le problème est la connexion imbriquée. J'appelle une instance locale du serveur SQL et cela ne génère pas le 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();
        }
    }
2
Iftikhar Ali

TransactionScope passe toujours à une transaction DTC si vous utilisez l'accès à plus d'une connexion à l'intérieur. Si le code ci-dessus peut fonctionner avec le DTC désactivé, le code ci-dessus ne peut fonctionner que si vous avez la même connexion à partir du pool de connexions à chaque fois.

"Le problème est que, sur la moitié de nos machines de développement, nous pouvons fonctionner avec MSDTC désactivé." Êtes-vous sûr que c'est désactivé;)

1
amateur