Как правильно настроить режим изоляции транзакций с помощью NHibernate? - PullRequest
2 голосов
/ 09 ноября 2010

Мы перешли с sqlite на встроенный сервер FireBird, потому что FB, кажется, поддерживает одновременные обновления базы данных, но иногда мы получаем следующее исключение:

2010-10-28 15:49:31,242 [56] ERROR NetworkCatcher.Entities.Agent.Server.RunResultManager - Failed to send result to server 32W2K3SP2VM-DEV. NHibernate.Exceptions.GenericADOException: could not update: ExecutionEntry#89_19_32W2K3SP2VM-DEV][SQL: UPDATE Run SET ExecutionId = ?, Source = ?, Destination = ?, ProtocolId = ?, Duration = ?, SampleCount = ?, StartTime = ?, ServerHostName = ?, SamplesSentToServer = ?, SampleInterval = ?, Parameters = ? WHERE Id = ?] ---> FirebirdSql.Data.FirebirdClient.FbException: deadlock
update conflicts with concurrent update
concurrent transaction number is 31632 --->
FirebirdSql.Data.Common.IscException: deadlock
update conflicts with concurrent update
concurrent transaction number is 31632
   at FirebirdSql.Data.Client.Native.FesDatabase.ParseStatusVector(IntPtr[]
statusVector)
   at FirebirdSql.Data.Client.Native.FesStatement.Execute()
   at FirebirdSql.Data.FirebirdClient.FbCommand.ExecuteCommand(CommandBehavior
behavior, Boolean returnsSet)
   at FirebirdSql.Data.FirebirdClient.FbCommand.ExecuteCommand(CommandBehavior
behavior)
   at FirebirdSql.Data.FirebirdClient.FbCommand.ExecuteNonQuery()
.
.
.

Ответ FB на это был «Почему вы думаете, что это ошибка?Это регулярный конфликт обновления, вызываемый одновременным обновлением одной и той же записи двумя транзакциями.Каков режим изоляции транзакций? »

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

Отображение обновляемого объекта:

<?xml version="1.0" encoding="utf-8" ?>
<hibernate-mapping xmlns="urn:nhibernate-mapping-2.2" auto-
import="true">
  <class name="NetworkCatcher.Entities.Agent.Server.ExecutionManager+ExecutionEntry,NC.Entities.Agent.Server" lazy="false" table="Run" entity-name="ExecutionEntry">
    <id name="Id" column="Id" type="string" >
      <generator class="assigned"/>
    </id>
    <property name="ExecutionId"/>
    <property name="Source"/>
    <property name="Destination"/>
    <property name="ProtocolId" type="string"/>
    <property name="Duration"/>
    <property name="SampleCount"/>
    <property name="StartTime"/>
    <property name="ServerHostName"/>
    <property name="m_samplesSentToServer" column="SamplesSentToServer" type="int" access="field" />
    <property name="SampleInterval"/>
    <property name="Parameters" type="binary"/>
  </class>
</hibernate-mapping>

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

Спасибо.

Ответы [ 2 ]

7 голосов
/ 09 ноября 2010

Режим изоляции транзакций обычно устанавливается в вашем файле hibernatem.cfg.xml:

ReadCommitted

http://www.nhforge.org/doc/nh/en/index.html#configuration-hibernatejdbc

Список допустимых значений и описания каждого из них можно найти в документации MSDN для System.Data.IsolationLevel:

http://msdn.microsoft.com/en-us/library/system.data.isolationlevel.aspx

Вам придется проверить документы FireBird, чтобы увидеть, какие из них он поддерживает.

Что касается вашего исключения, вы попадаете в тупик при обновлении записей, и это ожидается в реляционныхбазы данных.Вы должны быть готовы перехватить исключение взаимоблокировки и повторить операцию.Это не имеет ничего общего с NHibernate и связано с тем, как реляционные базы данных поддерживают транзакции.По сути, вы столкнулись с ситуацией, когда вы пытались обновить одни и те же две записи, A и B, в двух разных транзакциях.Одна транзакция имеет блокировку на A, а другая транзакция имеет блокировку на B. Каждая транзакция нуждается в блокировке для другой записи для завершения.Механизм базы данных выбирает жертву взаимоблокировки, откатывает свою транзакцию, отбрасывает исключение взаимоблокировки и позволяет завершить другую транзакцию.Если этого не сделать, обе транзакции будут ждать вечно (или время ожидания транзакции) для завершения другой транзакции.(Это может быть более сложный цикл записей, r1..rN и нескольких транзакций, но применимы одни и те же идеи.) Чистый результат заключается в том, что, как разработчик приложения, вы должны быть готовы к повторным попыткам операций, которые блокируют,используете ли вы NHibernate, сырой ADO.NET или любую другую технологию, использующую реляционную базу данных.

2 голосов
/ 09 ноября 2010

В дополнение к уровню изоляции по умолчанию, описанному Джеймсом, вы также можете установить уровень изоляции для отдельных транзакций, используя перегрузку ISession.BeginTransaction, которая принимает IsolationLevel.

Обратите внимание, что это неЭто не только для NH: это стандартная концепция ADO.NET, так что вы можете узнать больше об этом в MSDN.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...