Обработка ошибок транзакции NHibernate - PullRequest
7 голосов
/ 03 августа 2010

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

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

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

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

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

Ответы [ 4 ]

1 голос
/ 20 ноября 2010

Посмотрите на эту тему: http://n2cms.codeplex.com/Thread/View.aspx?ThreadId=85016

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

2010-02-17 21: 01: 41,204 1 ПРЕДУПРЕЖДЕНИЕ NHibernate.Util.ADOExceptionReporter - System.Data.SqlClient.SqlException: Журнал транзакций для базы данных 'databasename' заполнено. Выяснить почему пространство в журнале нельзя использовать повторно, см. столбец log_reuse_wait_desc в sys.databases

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

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

Проблема с использованием транзакции на запрос заключается в том, что ваша ISession получает много данных бухгалтерского учета, пока вы работаете, и все это является частью транзакции. Следовательно, база данных помечает данные (rols, cols, таблицы и т. Д.) Как участвующие в транзакции, в результате чего граф ожидания охватывает «сущности» (в смысле базы данных, а не в смысле 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 - номер исключения для транзакции изоляции моментального снимка, прерванной из-за конфликта обновления.

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

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

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

1 голос
/ 01 марта 2011

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

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

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

0 голосов
/ 27 января 2012

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

0 голосов
/ 03 августа 2010
...