Простая модель для тестирования:
class Registration
{
Date dateRequest;
}
в таблице есть одна запись с id = 1
1) Следующий код отрабатывает без проблем:
def testOne = {
def one = Registration.get(1);
def two = Registration.get(1);
one.dateRequest = new Date();
one.save(flush: true);
sleep(1000);
two.dateRequest = new Date();
two.save(flush: true);
}
По моей логике на строке two.save должен сработать exception, так как
при one.save поле `version` в таблице увеличивается на единицу и
данные считаются уже измененными.
2) Кучу не понятных exception, хотя должен быть быть только один (судя
по документации http://www.grails.org/doc/latest/guide/single.html#5.3.5
Pessimistic and Optimistic Locking)
def testTwo
{
def existReg = Registration.get(1);
if (params.test)
{
sleep(5000);
}
try
{
existReg.dateRequest = new Date();
existReg.save(flush: true);
}
catch(org.springframework.dao.OptimisticLockingFailureException e)
{
}
}
запускаю в следующей последовательности:
* /site/testTwo?test=1 (открывает запись и засыпает на 5 секунд)
* /site/testTwo (открывает запись, изменяет, сохраняет успешно)
Далее первый запрос через 5 секунд при сохранении отваливается с
ошибками:
Error 500: Object of class [Registration] with identifier [1]:
optimistic locking failed; nested exception is
org.hibernate.StaleObjectStateException: Row was updated or deleted by
another transaction (or unsaved-value mapping was incorrect):
[Registration#1]
Servlet: grails
URI: /app/grails/site/testTwo.dispatch
Exception Message: Row was updated or deleted by another transaction
(or unsaved-value mapping was incorrect): [Registration#1]
Caused by: Object of class [Registration] with identifier [1]:
optimistic locking failed; nested exception is
org.hibernate.StaleObjectStateException: Row was updated or deleted by
another transaction (or unsaved-value mapping was incorrect):
[Registration#1]
Class: Unknown
Кто как работает с оптимистичными блокировками ? И что делать?
Я написал небольшую статью по поводу блокировок:
когда вы "засыпаете", то jvm, точнее ее менеджер защиты блокирует все
созданные в текущем контексте исполнения объекты, чтобы кто-нибудь
случайно что не повредил. в то же время hibernate - это отдельный
механизм, отдельная "кухня", которая работает по своим правилам,
управляя менеджируемыми в ЕЕ текущем контексте объектами
(синхронизация данных с базой...), т.е. управляя текущей сессией. Вот
у вас и получается что hibernate-кухня ломится к заблокированным на
уровне jvm объектам (как работает hibernate -
http://java-money.blogspot.com/2009/10/hibernate.html).
варианты решения:
1. считывать объект после sleep (сделать это вам вообще ничто не
мешает)
2. пробовать блокировать ваш exitReg средствами hibernate (точно не
помню, но что-то вроде exitReg.lock())
а вообще засыпать внутри открытой транзакции - это очень неправильно.
они должны отрабатываться максимально быстро, иначе у вас будет
переполнение hibernate-сессии и при мало-мальских нагрузках (о которых
говорилось в приведенной ссылке) резко упадет производительность.
думайте как решить вашу задачу по другому
On Jul 23, 11:47 am, wholegroup <wholegr...@gmail.com> wrote:
> Помогите разобраться с оптимистичными блокировками.
>
> Простая модель для тестирования:
> class Registration
> {
> Date dateRequest;
>
> }
>
> в таблице есть одна запись с id = 1
>
> 1) Следующий код отрабатывает без проблем:
>
> def testOne = {
> def one = Registration.get(1);
> def two = Registration.get(1);
>
> one.dateRequest = new Date();
> one.save(flush: true);
>
> sleep(1000);
>
> two.dateRequest = new Date();
> two.save(flush: true);
>
> }
>
> По моей логике на строке two.save должен сработать exception, так как
> при one.save поле `version` в таблице увеличивается на единицу и
> данные считаются уже измененными.
>
> 2) Кучу не понятных exception, хотя должен быть быть только один (судя
> по документацииhttp://www.grails.org/doc/latest/guide/single.html#5.3.5
Пессимистические блокировки .lock() работают. Но т.к. в MySQL
блокировка записи SELECT FOR UPDATE не работает при включенном
autocommit'е (а он по-умолчанию включен), то код надо заключать в
транзакцию Model.withTransaction{}.
Сейчас все таки хочется разобраться с оптимистическими блокировками.
Сергей, если у вас есть время, вы можете протестировать мой код на
обработку исключения? Т.к. логически все правильно, но не работает.
Возможно в старых версиях работало и сейчас что-то сломалось.
registration.withSession { session ->
try
{
existReg.dateRequest = new Date();
existReg.save(flush: true);
}
catch(org.springframework.dao.OptimisticLockingFailureException e)
{
session.clear();
}
}
Это в принципе логично и соответствует документации, но было не совсем
очевидно (или "смотрю в книгу, вижу фигу" ;).