Проблемы с TransactionScope и Oracle
мы написали клиент C # 3.5, говорящий с базой данных Oracle (11g) с использованием ODP.NET.
Это приложение имеет пакетный процесс, в котором выполняется долго выполняемая задача, выполняющая различные вызовы базы данных в TransactionScope.
В нашей среде разработки все идет хорошо, но в среде UAT одного из наших клиентов (у которого много данных) возникают две чередующиеся (иногда одна, иногда другая ...) ошибки:
Невозможно подключиться к распределенной транзакцииТранзакция прервана. (внутреннее исключение: тайм-аут транзакции)В настоящее время мы используем тайм-ауткогда-нибудь для транзакции (для целей тестирования).
Запуск указанного процесса в среде 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 день, когда исключение возникает после ок. 10 минут?Можно ли предотвратить создание Oracle распределенной транзакции для транзакций, в которых строка подключения одинакова?Может ли быть так, что дополнительные издержки распределенной транзакции вызывают исключения, такие как транзакция, прерванная?Я обновил фрагмент кода, чтобы он лучше отражал ситуацию.
(кстати: вторая, вложенная транзакция необходима, поскольку DAL также отдельно сохраняет некоторые дочерние элементы, если они есть, и, конечно, весь элемент должен быть откатан, если что-то пойдет не так при сохранении дочерних элементов)
Надеемся, что с этим дополнением будет легче пролить свет на этот вопрос!