У нас возникает проблема при использовании NHibernate с распределенными транзакциями.
Рассмотрим следующий фрагмент:
//
// There is already an ambient distributed transaction
//
using(var scope = new TransactionScope()) {
using(var session = _sessionFactory.OpenSession())
using(session.BeginTransaction()) {
using(var cmd = new SqlCommand(_simpleUpdateQuery, (SqlConnection)session.Connection)) {
cmd.ExecuteNonQuery();
}
session.Save(new SomeEntity());
session.Transaction.Commit();
}
scope.Complete();
}
Иногда, когда сервер находится под экстремальной нагрузкой, мы увидим следующее:
- Запрос, выполняемый с cmd.ExecuteNonQuery, выбирается как жертва взаимоблокировки (мы можем видеть его в SQL Profiler), но исключение не возникает.
- session.Save завершается с сообщением об ошибке: "Операция недействительна для состояния транзакции".
- Каждый раз, когда этот код выполняется после этого, session.BeginTransaction терпит неудачу. Первые несколько раз меняется внутреннее исключение (иногда это исключение тупиковой ситуации, которое должно было быть поднято на шаге 1). В конце концов он стабилизируется так: "Сервер не смог возобновить транзакцию. Desc: 3800000177". или "Новый запрос не запускается, потому что он должен иметь действующий дескриптор транзакции".
Если оставить в покое, приложение в конечном итоге (через секунды или минуты) восстановится после этого условия.
Почему исключение тупиковой ситуации не сообщается на шаге 1? И если мы не сможем решить это, то как мы можем временно приостановить наше приложение?
Проблема была воспроизведена в следующих средах
- Windows 7 x64 и Windows Server 2003 x86
- SQL Server 2005 и 2008
- .NET 4.0 и 3.5
- NHibernate 3.2, 3.1 и 2.1.2
Я создал тестовое оборудование, которое иногда воспроизводит проблему для нас. Он доступен здесь: http://wikiupload.com/EWJIGAECG9SQDMZ