Обработка ошибок транзакций NHibernate

Наше приложение (которое использует NHibernate и ASP.NET MVC) при выполнении стресс-тестов вызывает множество ошибок транзакций NHibernate. Основные типы:

  1. Транзакция не подключена или была отключена
  2. Строка была обновлена ​​или удалена другой транзакцией (или сопоставление несохраненных значений было неправильным)
  3. Транзакция (идентификатор процесса 177) зашла в тупик при блокировке ресурсов с другим процессом и была выбрана жертвой тупиковой ситуации. Повторите транзакцию.

Может ли кто-нибудь помочь мне определить причину исключения 1? Я знаю, что мне нужно обрабатывать другие исключения в моем коде. Может ли кто-нибудь указать мне ресурсы, которые помогут мне эффективно справиться с этими ошибками?

В. Как мы управляем сеансами и транзакциями?

A. Мы используем Autofac. Для каждого запроса к серверу мы создаем новый контейнер запроса, который имеет сеанс в области существования контейнера. При активации сеанса мы начинаем транзакцию. Когда запрос завершается, мы фиксируем транзакцию. В некоторых случаях транзакция может быть огромной. Для упрощения каждый запрос сервера содержится в транзакции.


person Zuber    schedule 03.08.2010    source источник
comment
Как вы управляете сеансами и транзакциями?   -  person Diego Mijelshon    schedule 03.08.2010


Ответы (3)


Взгляните на эту ветку: http://n2cms.codeplex.com/Thread/View.aspx?ThreadId=85016

В основном то, что он говорит как возможную причину этого исключения:

2010-02-17 21: 01: 41,204 1 WARN NHibernate.Util.ADOExceptionReporter - System.Data.SqlClient.SqlException: журнал транзакций для базы данных «имя базы данных» заполнен. Чтобы узнать, почему пространство в журнале нельзя использовать повторно, см. Столбец log_reuse_wait_desc в sys.databases

Поскольку размер журнала транзакций пропорционален объему работы, проделанной во время транзакции, возможно, вам следует рассмотреть возможность помещения границ транзакции через обработку обработчиками команд команд в части транзакций, связанных с записью. Затем в сеансе # X вы загрузите состояние, которое хотите изменить, измените его и зафиксируйте, все как одну единицу работы в #X.

Что касается чтения, то у вас может быть другой ISession # Y, который читает данные; этот ISession может использоваться для пакетного чтения внутри, например RepeatableRead или что-то подобное с функцией Futures и может просто читать из кеша (если это действительно костыль). Это может помочь вам избавиться от «ошибок», которых нет; livelocks, взаимоблокировки и транзакции-жертвы.

Проблема с использованием транзакции на запрос заключается в том, что ваш ISession получает много данных бухгалтерского учета во время вашей работы, и все они являются частью транзакции. Следовательно, база данных отмечает данные (роли, столбцы, таблицы и т. Д.) Как участвующие в транзакции, в результате чего граф ожидания охватывает «сущности» (в смысле базы данных, а не в смысле DDD), которые на самом деле не являются частью транзакционной границы команды, принятой вашим приложением.

Для записи (другие люди гуглили), у Фабио был сообщение, посвященное работе с исключениями на уровне данных. Цитируя часть его кода;

public class MsSqlExceptionConverterExample : ISQLExceptionConverter
{
  public Exception Convert(AdoExceptionContextInfo exInfo)
  {
      var sqle = ADOExceptionHelper.ExtractDbException(exInfo.SqlException) as SqlException;
      if(sqle != null)
      {
          switch (sqle.Number)
          {
              case 547:
                  return new ConstraintViolationException(exInfo.Message,
                      sqle.InnerException, exInfo.Sql, null);
              case 208:
                  return new SQLGrammarException(exInfo.Message,
                      sqle.InnerException, exInfo.Sql);
              case 3960:
                  return new StaleObjectStateException(exInfo.EntityName, exInfo.EntityId);
          }
      }
      return SQLStateConverter.HandledNonSpecificException(exInfo.SqlException,
          exInfo.Message, exInfo.Sql);
  }
}
  • 547 - это номер исключения для конфликта ограничений.
  • 208 - это номер исключения для недопустимого имени объекта в SQL.
  • 3960 - это номер исключения для транзакции изоляции моментального снимка, прерванной из-за конфликта обновления.

Так что, если вы сталкиваетесь с проблемами параллелизма, подобными описанным вами; помните, что они сделают ваш ISession недействительным и вам придется обращаться с ними, как указано выше.

Часть того, что вы, возможно, ищете, - это CQRS, где у вас есть отдельные стороны чтения и записи. Это может помочь: http://abdullin.com/cqrs/, http://cqrsinfo.com.

Итак, чтобы подвести итог; ваши проблемы могут быть связаны с тем, как вы обрабатываете свои транзакции. Также попробуйте запустить select log_wait_reuse_desc from sys.databases where name='MyDBName' и посмотрите, что это вам даст.

person Henrik    schedule 20.11.2010

В этой ветке есть объяснение: http://groups.google.com/group/nhusers/browse_thread/thread/7f5fb68a00829d13

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

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

person Samuel Otter    schedule 01.03.2011

Я знаю, что это сообщение было некоторое время назад, и предполагаю, что вы его исправили, но похоже, что у вас есть проблемы с совместным доступом к потокам с NHibernate ISession, которые не являются потокобезопасными. Обычно один поток начинает транзакцию, а другой пытается закрыть ее, вызывая всевозможный хаос.

person Samuel Goldenbaum    schedule 27.01.2012