TransactionScope автоматично переходить до MSDTC на деяких машинах?


284

У нашому проекті ми використовуємо TransactionScope, щоб переконатися, що рівень доступу до даних виконує дії в транзакції. Ми прагнемо не вимагати включення послуги MSDTC на наших машинах кінцевого користувача.

Проблема полягає в тому, що на половині наших машин для розробників ми можемо працювати з відключеною MSDTC. У другій половині повинно бути включено або вони отримують повідомлення про помилку "MSDTC на [SERVER] недоступний" .

По-справжньому, я чухаю голову, і я серйозно розглядаю можливість повернення до домашнього розгорнутого рішення TransactionScope, заснованого на об'єктах транзакцій ADO.NET. Це , здавалося б , з розумом - той же код , який працює (і не нагнітати) на половину нашого розробника робить нагнітати на іншому розробник.

Я сподівався на кращу відповідь Trace, чому транзакція ескальована до DTC, але, на жаль, це не відбувається.

Ось зразковий біт коду, який спричинить неполадки, на машинах, які намагаються ескалацію, він намагається ескалації на другому з'єднанні. Відкрийте () (і так, немає іншого відкритого підключення на той час.)

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

Ми справді копалися і намагалися розібратися в цьому. Ось деяка інформація про машини, над якими працює:

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

Розробники, над якими він не працює:

  • Dev 4: Windows 7 x64, SQL2008 SQL2005
  • Dev 5: Windows Vista x86, SQL2005
  • Dev 6: Windows XP X86, SQL2005
  • Мій домашній ПК: Windows Vista Home Premium, x86, SQL2005

Варто додати, що всі машини, прагнучи вирішити проблему, були повністю виправлені з усім, що доступно в Microsoft Update.

Оновлення 1:

На цій сторінці ескалації транзакцій MSDN зазначено, що наступні умови призведуть до ескалації транзакції до DTC:

  1. Принаймні один довговічний ресурс, який не підтримує однофазні повідомлення, зарахований до трансакції.
  2. Принаймні два довговічні ресурси, що підтримують однофазні повідомлення, зараховані до трансакції. Наприклад, зарахування єдиного з'єднання з не спричиняє просування транзакції. Однак щоразу, коли ви відкриваєте друге підключення до бази даних, що призводить до занесення бази даних, інфраструктура System.Transaction виявляє, що це другий довговічний ресурс транзакції, і передає його до транзакції MSDTC.
  3. Викликається запит на "маршалка" транзакції на інший домен програми або інший процес. Наприклад, серіалізація об'єкта транзакції через межу домену програми. Об'єкт транзакції порівнюється за значенням, тобто будь-яка спроба передати його через межу домену програми (навіть у тому самому процесі) призводить до серіалізації об'єкта транзакції. Ви можете передати об'єкти транзакцій, здійснивши виклик за віддаленим методом, який приймає транзакцію як параметр, або ви можете спробувати отримати доступ до віддаленого компонента, що обслуговується. Це серіалізує об'єкт транзакції і призводить до ескалації, як коли транзація серіалізується через домен програми. Він розповсюджується, і локальний менеджер транзакцій вже не є адекватним.

Ми не відчуваємо №3. №2 цього не відбувається, оскільки одночасно існує лише одне з'єднання, а також до одного "довговічного ресурсу". Чи є спосіб, що №1 міг би відбуватися? Якась конфігурація SQL2005 / 8, через яку вона не підтримує однофазні сповіщення?

Оновлення 2:

Особисто повторно досліджено всі версії SQL Server - "Dev 3" насправді має SQL2008, а "Dev 4" насправді SQL2005. Це навчить мене ніколи більше не довіряти своїм колегам. ;) Через цю зміну даних я майже впевнений, що ми знайшли нашу проблему. Наші розробники SQL2008 не мали проблеми, тому що в SQL2008 включено велику кількість дивовижних, яких немає в SQL2005.

Це також говорить мені, що, оскільки ми будемо підтримувати SQL2005, ми не можемо використовувати TransactionScope, як ми були, і якщо ми хочемо використовувати TransactionScope, нам потрібно буде передавати один об'єкт SqlConnection навколо ... що здається проблематичним в ситуаціях, коли SqlConnection не може бути легко пройдений навколо ... він просто пахне глобальним екземпляром SqlConnection. П'ю!

Оновлення 3

Просто для уточнення тут у питанні:

SQL2008:

  • Дозволяє безліч з'єднань в межах одного TransactionScope (як показано у наведеному вище прикладі коду.)
  • Застереження №1: Якщо ці кілька SqlConnections вкладені, тобто два або більше SqlConnections відкриваються одночасно, TransactionScope негайно перейде до DTC.
  • Застереження №2: Якщо додатковий SqlConnection відкриється для іншого "довговічного ресурсу" (тобто іншого сервера SQL,), він негайно перейде до DTC

SQL2005:

  • Не дозволяє декілька з'єднань протягом одного TransactionScope, періоду. Він посилиться, коли / якщо буде відкрито друге SqlConnection.

Оновлення 4

В інтересах зробити це питання ще більше безладу корисно, і просто заради більшої ясності, ось як ви можете отримати SQL2005 нагнітати ДТК з одного SqlConnection :

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

Мені це здається порушеним, але, мабуть, я можу зрозуміти, якщо кожен дзвінок на SqlConnection.Open()хапається з пулу зв’язків.

"Чому це може статися, хоча?" Добре, якщо ви використовуєте SqlTableAdapter проти цього з'єднання перед його відкриттям, SqlTableAdapter відкриє та закриє з'єднання, фактично закінчивши транзакцію для вас, тому що тепер ви не можете його знову відкрити.

Отже, для успішного використання TransactionScope з SQL2005 вам потрібно мати якийсь об'єкт глобального з’єднання, який залишається відкритим з точки першого TransactionScope, поки він більше не потрібен. Крім кодового запаху об'єкта глобального з'єднання, відкриття з'єднання спочатку та закриття його останнім суперечить логіці відкриття з'єднання якомога пізніше та закриття його якнайшвидшого.


Чи можете ви розгорнути "Зробити тут інші речі, які можуть або не включатимуться в навколишню транзакцію". Звичайно, що там сильно впливає на поведінку коду?
RichardOD

2
"# 2 не відбувається, тому що існує лише одне з'єднання одночасно" - №2 не говорить про те, що друге з'єднання має бути відкритим одночасно, тільки що його потрібно зарахувати до тієї самої транзакції.
Джо

3
Дякую вам за повідомлення про оновлення 4, що показує, як ескалація може відбутися лише за допомогою одного SqlConnection. Це саме те, на що я зіткнувся, незважаючи на ретельне забезпечення того, що використовується лише одна SqlConnection. Приємно знати, що це комп'ютер, який божевільний, а не я. :-)
Оран Деннісон

Що стосується об'єднання підключень, якщо у нас є декілька підключень (і вкладене, якщо потрібно), якщо ми відкриваємо і закриваємо по черзі, чи ми використовуємо 1 реальний ресурс пулу консекцій або 1 за з'єднання, я намагаюся раціоналізувати це, щоб визначити чи належним чином проаналізовано "придатне" підключення (чого я хотів би уникнути)
brumScouse

1
Вкладені з’єднання в одній області транзакцій сприятимуть розподілу транзакцій. З сервера SQL 2008 і вище декілька (не вкладених) з'єднань в одній і тій же області транзакцій не сприятимуть розподіленому трансакітону.
PreguntonCojoneroCabrón

Відповіді:


71

SQL Server 2008 може використовувати декілька SQLConnections в одному TransactionScopeбез ескалації, якщо з'єднання не відкриті одночасно, що призведе до декількох "фізичних" TCP-з'єднань і, таким чином, вимагає ескалації.

Я бачу, що деякі з ваших розробників мають SQL Server 2005, а інші - SQL Server 2008. Ви впевнені, що ви правильно визначили, які з них ескаліруються, а які ні?

Найбільш очевидним поясненням буде те, що розробники з SQL Server 2008 - це ті, що не збільшуються.


Так, деталі є правильними, і хтось насправді дивиться код? У межах транзакції є два з'єднання, однак існує лише одне з'єднання, встановлене та відкрите в один момент часу. Також ні, DTC не працює на машинах, які працюють.
Yoopergeek

1
"однак, існує лише одне колись встановлене і відкрите за один момент часу" - чому це актуально? Якщо в SQL2005 ви відкриваєте більше одного з'єднання в межах транзакції, ви збільшуватиметесь, незалежно від того, залишаються вони одночасно. Що логічно, якщо ви задумаєтесь.
Джо

Ви та члени мешканців зараз нагадали про мене, і мені дуже хочеться почати працювати в понеділок та уважніше оглянути їхні окремі машини та переконатися, що версії SQL Server такі, як повідомлялося раніше.
Yoopergeek

19
Ви з працівниками права. У мене на всьому обличчі яйце. Дякую за те, що вдарили мене палицею. :) Оскільки ти був першим, ти отримуєш відповідь. Хочеться додати ще одну точку уточнення - SQL2008 дозволяє відкрити кілька підключень, але не одночасно. Все ще може бути лише одне єдине з'єднання, відкрите в будь-який момент часу, або TransactionScope перейде до DTC.
Yoopergeek

@Yoopergeek Я міг би переконатися, що ваше "не одночасно" важливо, і відредагував відповідь @Joe відповідно. Моніторинг TCP-з'єднань під час тестування показав, що старе TCP-з'єднання буде повторно використане, коли з'єднання не використовуються одночасно, і, таким чином, TransactionScopeможна зробити з єдиним COMMITна стороні сервера, що зробить ескалацію зайвою.
Євген Бересовський

58

Результат мого дослідження на тему:

введіть тут опис зображення

Див. Розділ Уникнення небажаної ескалації до розподілених транзакцій

Я все ще розслідую ескалаційну поведінку Oracle: Чи ескалації транзакцій, що охоплюють декілька підключень до однієї БД, до DTC?


1
Дякуємо, що поділилися своїми дослідженнями. Це справді допомогло. Ще один швидкий запит. Чим відрізняється TransactionScope () від sqlConnection.BeginTransaction ()?
Бейг

Відповідно до цього запиту на функцію , ODAC 12C тепер повинен вести себе як SQL 2008, не сприяючи розповсюдженню при використанні послідовних підключень до одного і того ж джерела даних.
Фредерік

31

Цей код буде викликати ескалацію при підключенні до 2005 року.

Перевірте документацію на MSDN - http://msdn.microsoft.com/en-us/library/ms172070.aspx

Рекламні транзакції в SQL Server 2008

У версії 2.0 платформи .NET Framework та SQL Server 2005 відкриття другого з'єднання всередині TransactionScope автоматично сприятиме транзакції до повної розподіленої транзакції, навіть якщо обидва з'єднання використовували однакові рядки з'єднання. У цьому випадку розподілена транзакція додає зайві накладні витрати, що знижує продуктивність.

Починаючи з SQL Server 2008 та версії 3.5 версії .NET Framework, локальні транзакції більше не рекламуються до розподілених транзакцій, якщо інше з'єднання буде відкрито в транзакції після закриття попередньої транзакції. Це не потребує змін у вашому коді, якщо ви вже використовуєте об'єднання з'єднань та зараховуєтесь до транзакцій.

Я не можу пояснити, чому Dev 3: Windows 7 x64, SQL2005 успішний і Dev 4: Windows 7 x64 виходить з ладу. Ви впевнені, що це не навпаки?


10

Я не знаю, чому цю відповідь було видалено, але, здається, є якась відповідна інформація.

відповів 4 серпня 10.10 о 17:42 Едуардо

  1. Встановіть Enlist = false у рядку з'єднання, щоб уникнути автоматичного зарахування на транзакцію.

  2. Вручну зарахуйте з'єднання як учасників у межах транзакції. [ оригінальна стаття застаріла] або зробити це: як запобігти автоматичному просуванню MSDTC [archive.is]


msdn.microsoft.com/en-us/library/ms172153%28v=VS.80%29.aspx не знайдено, Visual Studio 2005 Документація на пенсію
Kiquenet

2

Я не надто впевнений, чи це вкладене з'єднання. Я викликаю локальний примірник SQL-сервера, і він не генерує 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();
        }
    }

Яке видання SQL Server ви використовуєте? Цікаво, чи потрібно оновити відповідь @Peter Meinl, щоб відобразити будь-які зміни, внесені у 2008R2 та / або Denali.
Yoopergeek

Я використовую SQL Server 2008 R2.
Іфтіхар Алі

Цікаво, чи краще у R2 2008 року? Відповідь @hwiechers також змушує мене замислитись, чи не запобігає ескалації версія Framework, яку ви збираєте. Нарешті, мені цікаво, чи має місце місцевий екземпляр R2. Я б хотів, щоб у мене був час / ресурси, щоб дослідити, як це змінилося з випуском 2008 року R2 та SQL Server 2012.
Yoopergeek

Не впевнені, чи це вкладене з'єднання? хаха ... добре цвіте видаліть його тоді !, чому на землі люди гніздяться, використовуючи заяви, коли це зовсім не потрібно, я ніколи не знаю.
Пол Захра

1

TransactionScope завжди перетворюється на транзакцію DTC, якщо ви використовуєте доступ більше ніж 1 з'єднання всередині. Єдиний спосіб, як описаний вище код може працювати з відключеним DTC, це якщо ви з великим шансом отримаєте те саме з'єднання з пулу з'єднань обох разів.

"Проблема в тому, що на половині наших машин розробників ми можемо працювати з відключеною MSDTC." Ви впевнені, що його вимкнено;)


0

Переконайтеся, що для вашого connectionString не встановлено об'єднання в false. Це призведе до нового з'єднання для кожного нового SqlConnection в TransactionScope та ескалації його до DTC.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.