Locks и IsolationLevel

224
01 июня 2018, 19:10

У меня есть такой код модуля статистики:

using(TransactionScope scope = new TransactionScope(TransactionScopeOption.Required, new TransactionOptions {IsolationLevel = IsolationLevel.RepeatableRead})) 
{
     PersonDayStatisticsUnit pdsu = db.PersonDayStatisticsUnits.FirstOrDefault(x => x.UnitId == su.Id && x.Date == now && x.PersonAnonimousGuid == personGuid);
     if (pdsu == null) 
     {
         pdsu = new PersonDayStatisticsUnit() 
         {
             UnitId = su.Id,
                 Count = 1,
                 Date = now,
                 PersonAnonimousGuid = personGuid
         };
         db.PersonDayStatisticsUnits.Add(pdsu);
         ret = true;
     } 
     else 
     {
         pdsu.Count++;
         ret = false;
     }
     db.SaveChanges();
     scope.Complete();
 }

Этот код работает в ASP.NET MVC 5 контексте.

Обнаружил что этот код падает, с ошибкой, что не может создать запись с дублирующимися ключами.

В чём ошибка я понял, что видимо два потока зашли в транзакцию и попытались создать одинаковый объект PersonDayStatisticsUnit, db.PersonDayStatisticsUnits.Add(pdsu).

То есть что-то я недодумал в коде...

Как я понимаю весь этот код можно просто поместить в lock, это должно решить проблему.

Нужен ли здесь lock и какое наилучшее решение ситуации? Как вообще соотносятся транзакции с lock-ами, можно или нужно их делать одновременно, или же тут надо просто сделать другой вид транзакции IsolationLevel?

Answer 1

Ни выставление IsolationLevel, ни lock (в общем случае) тут не помогут.

lock поможет в случае, если у вас ровно один экземпляр приложения. Т.е. это хорошее решение, если ваше приложение - небольшое, и его не планируется масштабировать.

В общем случае - если у вас два или более серверов - лок не поможет.

IsolationLevel не поможет потому, что он работает не так, как вы ожидаете. Он контролирует две вещи:

  • Какие блокировки устанавливаются каждым запросом
  • Как долго блокировки удерживаются

Если при попытке поставить блокировку на ресурсе (таблице, строке, разделе) стоит несовместимая блокировка - запрос ждет, пока именно эта блокировка будет снята. Например, можно поставить два S лока одновременно, S и U одновременно, но нельзя поставить два U.

Это достаточно подробно и обширно расписано в Transaction Locking and Row Versioning Guide, но если коротко, то

  • При SELECT ставятся S-локи
  • В зависимости от уровня блокировки - они удерживаются или на момент чтения, или до конца транзации
  • S-локи совместимы друг с другом

Так что при любом уровне изоляции транзакций вы можете делать одновременные выборки.

Если вам нужно запретить одновременные выборки - вам нужно - ставить при выборке не S-lock, а U-lock или X-lock - удерживать его до конца транзакции

Тип и уровень блокировки можно поменять подсказкой в тексте запроса (TABLE HINT)

Т.е. вам или в первом SELECT, или перед ним нужно выполнить SQL вида

SELECT top 1 somecolumn FROM PersonDayStatisticsUnits
WHERE (xlock, tablock)

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

Answer 2

Я бы в таком случае попробовал установить IsolationLevel Serializable. Если не поможет, тогда вероятно нужен lock.

READ ALSO
Парсер страницы

Парсер страницы

когда пытаюсь вывести значения в CheckedListBox значения, на переменную node2 выскакивает ошибка SystemNullReferenceException: "Ссылка на объект не указывает на экземпляр...

346
Как вернуть строку черз маршалинг

Как вернуть строку черз маршалинг

Нужно вернуть строку с кода написанного на С++ в С#

296
Реализация корзины магазина в EntityFramework

Реализация корзины магазина в EntityFramework

База данных интернет магазинаЕсть два класса

241
Remove range в EF Core и транзакция

Remove range в EF Core и транзакция

Когда мы производим удаление с помощью Remove range в ef core у нас все выполняется в транзакции? Если, что то не удалится из списка удаляемых объектов...

243