NHibernate - пессимистическая блокировка не работает

Ответьте на этот другой вопрос.

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

Я делаю очень простой тест: у меня работают два отдельных сайта, каждый из которых увеличивает счетчик в 500 раз. Я запускаю их одновременно. В конце концов, я ожидаю, что определенный столбец в моей таблице имеет, как вы уже догадались, значение 1000.

Вот код. Конечно, это не рабочий код, но тестовый код или нет, он все равно должен работать, верно?

for (int i = 0; i < 500; i++)
{
  var tx = this.userRepo.Session.BeginTransaction();
  var user = this.userRepo.GetById(42);
  user.Counter++;
  userRepo.Save(user);
  tx.Commit();
}

Метод GetById использует LockMode.Upgrade:

public T GetById(int id)
{
  T obj = Session.Get<T>(id, LockMode.Upgrade);
  return obj;
}

Теперь, используя NHProfiler, я вижу следующий оператор SQL:

SELECT Id FROM 'User' WHERE Id = 42 for update

но результатом является значение около 530, то есть примерно половина обновлений потеряна из-за параллелизма. Что я делаю неправильно? В этом тесте я отключил кеш второго уровня. Я использую неправильный режим блокировки? Должен ли я указывать уровень изоляции? Что-нибудь еще? Заранее спасибо.

EDIT: конфигурация FluentNhibernate:

Fluently.Configure()
.Database(MySQLConfiguration.Standard.ConnectionString(connectionstring))
.Mappings(m => assemblyTypes.Select(t => t.Assembly).ToList().ForEach(a => m.FluentMappings.AddFromAssembly(a)))
.ExposeConfiguration(c => c.Properties.Add("hbm2ddl.keywords", "none"));

person Razzie    schedule 23.11.2010    source источник
comment
Можете ли вы опубликовать свой код конфигурации Nhibernate?   -  person UpTheCreek    schedule 23.11.2010
comment
@UpTheCreek: используя FluentNH, не уверен, что вы хотите увидеть, но я включил это   -  person Razzie    schedule 23.11.2010


Ответы (1)


Чтобы LockMode.Upgrade работал, все транзакции должны быть заключены в транзакцию, потому что LockMode.Upgrade блокирует ее в текущей транзакции.

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

Оптимистическая блокировка применяется не к одному оператору, а к нескольким транзакциям, которые отделены друг от друга. Пример:

  1. Начать транзакцию;

  2. Получить запись по Id = 42;

  3. Завершить транзакцию.

Затем вне транзакции увеличьте Counter.

После этого:

  1. Начать транзакцию;

  2. Получить запись по Id = 42;

  3. Проверить, не изменился ли счетчик по сравнению со значением, полученным в первой транзакции;

    а. Если он не изменился, обновите счетчик с увеличенным значением;

    б. Если оно изменилось, обработайте измененное значение.

  4. Завершить транзакцию.

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

Кстати: механизм проверки (изменился ли Counter за это время) может автоматически обрабатываться NHibernate.

person Pieter van Ginkel    schedule 23.11.2010
comment
Посмотрите на мой код — в конце я использую BeginTransaction() и Commit(). Это то, что вы имеете в виду, верно? - person Razzie; 23.11.2010
comment
Извините, вы правы. Какой у вас режим сериализации (настройка Connection.isolation SessionFactory или первый параметр BeginTransaction). См. msdn.microsoft.com/en-us/library/ для получения дополнительной информации об их значении. Попробуйте поиграть с ними и посмотрите, будет ли это иметь значение. Посмотрите, что происходит с Serializable, потому что это должно гарантировать, что это работает должным образом. - person Pieter van Ginkel; 23.11.2010
comment
хм, возможно, использование цикла for также вызывало проблемы. Кажется, что без цикла for, но с использованием Thread.Sleep для обеспечения параллелизма я получаю хорошие результаты, используя IsolationLevel.RepeatableRead и LockMode.Upgrade. Тем не менее, если вы можете пояснить, как эти разные механизмы и эти типы различий работают вместе, мы будем очень признательны. - person Razzie; 23.11.2010
comment
Мне придется отослать вас к документации по этим уровням изоляции, потому что это слишком много, чтобы обрабатывать это в комментарии :). Однако обратите внимание, что сама ссылка MSDN также содержит некоторую информацию, но en.wikipedia.org/wiki/ Isolation_(database_systems), возможно, предоставит больше информации. - person Pieter van Ginkel; 23.11.2010