Проблемы с TransactionScope и Oracle - PullRequest
4 голосов
/ 14 июля 2010

мы написали клиент C # 3.5, который общается с базой данных Oracle (11g), используя ODP.NET.

В этом приложении есть пакетный процесс, в котором выполняется длительная задача, выполняющая различные вызовы базы данныхa TransactionScope.

В нашей среде разработки все идет хорошо, но в среде UAT одного из наших клиентов (у которого много данных) возникают две чередующиеся (иногда одна, иногда другая ...) ошибки:

  1. Невозможно подключиться к распределенной транзакции
  2. Транзакция прервана.(внутреннее исключение: тайм-аут транзакции)

В настоящее время мы используем тайм-аут один день для транзакции (в целях тестирования).

Запуск указанного процессав среде UAT вызывает остановку после прибл.10 минут с одним из вышеперечисленных исключений, поэтому не приближайтесь к значению тайм-аута.

Вот фрагмент трассировки стека для второй ошибки:

at System.Transactions.TransactionStatePromotedAborted.CreateAbortingClone(InternalTransaction tx)
   at System.Transactions.DependentTransaction..ctor(IsolationLevel isoLevel, InternalTransaction internalTransaction, Boolean blocking)
   at System.Transactions.Transaction.DependentClone(DependentCloneOption cloneOption)
   at System.Transactions.TransactionScope.SetCurrent(Transaction newCurrent)
   at System.Transactions.TransactionScope.PushScope()
   at System.Transactions.TransactionScope..ctor(TransactionScopeOption scopeOption)
   at System.Transactions.TransactionScope..ctor()
   at Application.Domain.DataAccess.Oracle.EntityDaoBase`2.SaveItem(TEntity item, EntityReference`1 user)

Процесс пытается сохранить элемент вБД в рамках транзакции, но трассировка стека показывает, что конструктор вызван для класса TransactionScope, то есть он создает новый TransactionScope.

Прав ли я до сих пор?

Потому что я неОн не очень хорошо знает внутреннюю работу TransactionScope, но, похоже, когда вы вызываете метод в области действия, он создаст новую транзакцию (предположительно наследуя от внешней транзакции).

Может быть, еслиЯ прав, что эта новая транзакция не наследует правильный тайм-аут (но по умолчанию), так что вложенная транзакция вызовет это исключение тайм-аута?

Если нет, какие-либо мысли о том, что это может быть?Примечательно, что в методах, вызываемых из внешней транзакции, не определены вложенные транзакции.

Любая помощь будет принята с благодарностью!

Редактировать 1:

Упрощенный фрагмент кода функции:

public void SomeLengthyBatchProcess()
{
   using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0, 0)))
   {
       foreach (var item in Items)
       {
          SaveItemToDB(item);
       }

       transaction.Complete();
   }
}

public void SaveItemToDB(object item)
{
   using (var transaction = new TransactionScope(TransactionScopeOption.Required, new TimeSpan(1, 0, 0, 0)))
   {
       // Performing data persistency here

       transaction.Complete();
   }
}

Редактировать 2:

Ладно, как оказалось, там есть вложенная транзакция, происходящая в методе SaveItemToDB.Немного покопавшись в коде, созданном коллегой, я увидел, что у него определен собственный TransactionScope, но без параметров и времени ожидания.

После изменения этого метода, чтобы он имел те же параметры, что и время ожидания, я запустил кодснова на сервере клиента и все равно не повезло (снова транзакция прервала ошибку с тайм-аутом).

Итак, мои вопросы теперь следующие:

  1. Нужно ли определять тайм-аутзначения для вложенных транзакций, или они наследуют это от внешней транзакции?
  2. Как возможно, что может возникнуть исключение тайм-аута, когда настройка тайм-аута (предположительно, помимо внутренней работы, о которой я не знаю)То же самое для всех областей транзакции и имеет значение тайм-аута, определенное в 1 день, где исключение происходит после прибл.10 минут?
  3. Можно ли предотвратить создание Oracle распределенной транзакции для транзакций с одинаковой строкой соединения?
  4. Может ли быть, что дополнительные издержки распределенной транзакции вызовут исключения, подобныеодна транзакция прервана?

Я обновил фрагмент кода, чтобы он лучше отражал ситуацию.

(кстати: вторая, вложенная транзакция необходима, поскольку DAL также отдельносохраняются некоторые дочерние элементы, если они есть, и, конечно, весь элемент должен быть откатан, если что-то пойдет не так, при сохранении дочерних элементов)

Надеюсь, с этим дополнением будет легче пролить светпо этому вопросу!

Ответы [ 6 ]

5 голосов
/ 26 июля 2010

Поскольку мы не смогли найти решение, мы решили прекратить использование TransactionScope для наших целей и самостоятельно организовать откат.

Я считаю, что TransactionScope и Oracle не очень хорошо сочетаются, возможно, SQL Server справляется с этим лучше, но это не вариант для нас.

Спасибо за чтение.

1 голос
/ 15 июня 2011

тайм-аут транзакции по умолчанию в machine.config составляет 10 минут ... возможно, именно поэтому вы используете тайм-аут.

0 голосов
/ 20 февраля 2015

Сначала решаем основную проблему:

Как возможно, что исключение тайм-аута может произойти, если значение тайм-аута (предположительно, помимо внутренних операций, о которых я не знаю) одинаково для всех областей транзакции и имеет значение тайм-аута, определенное в 1 день, гдеисключение происходит после ок.10 минут?

Существует свойство TransactionManager.MaximumTimeout, которое является верхней границей того, что вы пытаетесь установить с помощью своей области.В вашей системе он установлен на 10 minutes, но в соответствии с документацией

Это значение можно установить в разделе MachineSettingsSection файла конфигурации.

Что касается других вопросов:

  1. Необходимо ли определять значения тайм-аута для вложенных транзакций или они наследуют их от внешней транзакции?

Область действия , инициирующая транзакцию (т. Е. Любая область действия RequiresNew, любая внешняя область действия Required и любая область действия Required, которая имеет область действия Suppress на один уровень выше стека вложений) установит transaction timeout, и что касается моего чтения источников , этот таймаут не зависит от вложенных областей действия .

Однако каждая вложенная область , участвующая в существующей транзакции (т. Е. Любая область Required, имеющая область Required или RequiresNew на один уровень вверх по стеку), установит свой собственный тайм-аут области , который запускается в дополнение к тайм-ауту транзакции упомянуто выше.

Тайм-ауты транзакций и тайм-ауты области внутренне реализованы по-разному, но если какой-либо из этих тайм-аутов достигнет, транзакция должна быть Complete() dбудет отменен.

Кстати, вышеупомянутое TransactionManager.MaximumTimeout применяется только к тайм-аутам транзакций . Тайм-ауты не имеют верхней границы.Не то, чтобы это действительно имело значение, так как в любом случае самое короткое время ожидания.

Можно ли предотвратить создание Oracle распределенной транзакции для транзакций, в которых строка подключения одинакова?

Пока у вас открыто только одно "физическое" соединение с БД в любойВ определенный момент времени область не будет расширяться до DTC.Если я правильно помню, это работает с Oracle ODP.Net, несмотря на ( this ), казалось бы, утверждающее обратное (может быть, это не работало с версией в то время?).

Вы можетеили, возможно, не сможет предотвратить одновременные соединения даже с вложенными областями и для разных баз данных (если они находятся на одном сервере).

0 голосов
/ 16 октября 2014

хотя старый вопрос, надеюсь, этот ответ поможет ... это особенно происходит для длительных транзакций, потому что базовый IDbConnection не остается открытым в течение более длительного периода времени и создается новое соединение для частей области операций (пул соединений). по той же причине длинная транзакция может завершиться успешно, если будет возвращено и использовано то же открытое соединение, иначе оно завершится неудачно. Единственное решение для этого - контролировать создание соединения и гарантировать, что используется только одно соединение.

0 голосов
/ 28 сентября 2014

Я знаю, что это старый вопрос, но я добавлю к нему, так как я видел это совсем немного.

Вы используете RAC?Работали ли вы с администратором базы данных, чтобы увидеть, не испытываете ли вы блокировки / блокировки.Я использовал System.Transactions с Oracle в течение многих лет, и единственные случаи, когда у меня были подобные проблемы, - это когда мы использовали RAC и требовалось выполнить дополнительную настройку.

Вот что происходит: вы запускаете транзакцию иоткрываются соединения во время транзакции (что нормально).Однако служба oracle не настроена для распределенной обработки транзакций (это простая опция флажка в службе).Таким образом, дополнительные соединения начинают охватывать более одного экземпляра в кластере RAC, и связанные транзакции не знают друг друга, в результате чего процесс .net блокирует сам себя.

Это простое исправление.Службе оракула, которую вы используете, нужно только включить DTP.

0 голосов
/ 14 июля 2010

Можно ли показать фрагмент кода? Из того, что вы упомянули Единственное, что я смог найти, было связано с System.Transactions. Обсуждение здесь . Конечно, их «решение» - убедиться, что вы используете хотя бы ODP.NET 11.1.0.6.20 или выше.

...