Андрюшка (jonny81) wrote in ru_grails,
Андрюшка
jonny81
ru_grails

Optimistic Locking

Помогите разобраться с оптимистичными блокировками.

Простая модель для тестирования:
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

Кто как работает с оптимистичными блокировками ? И что делать ?
  • Post a new comment

    Error

    default userpic

    Your IP address will be recorded 

  • 8 comments
В первом случае будет один и тот же объект. Закэширован в сессии хибернейта. Никаких проблем с локами.
Во-втором случае, ну пользуйте транзакции.

Optimistic lock он на то и optimistic что считается что коллизии встречаются редко, и рассматриваются как исключительные ситуации.
хочется отработать нормально такую исключительную ситуацию.
Транзакции я попробую, но если предположить, что между началом и концом транзакции есть большой код, который выполняется значительное время, то это не решение. Надо все таки на уровне записи блокировку делать.

Поэтому, решил попробовать пессимистические блокировки. И как это не странно, но тоже не работает.
Добавил в модель 'version false', и Registration.get(1) заменил на Registration.lock(1). Запускаю второй testTwo:
* /site/testTwo?test=1 (открывает/блокирует запись и засыпает на 5 секунд. )
* /site/testTwo (открывает/блокирует запись, изменяет, сохраняет успешно. а должен ждать пока проснется первый поток и завершит обновление записи)
После просыпания первый поток изменяет и сохраняет запись успешно.

Копаюсь дальше.

БД на MySQL с InnoDB
А какие запросы идут?
Кстати, а вы уверены что 2й поток отрабатывает раньше?
...
Hibernate:
select
registrati0_.id as id41_0_,
registrati0_.date_request as date2_41_0_,
registrati0_.email as email41_0_
from
cmn_registration registrati0_
where
registrati0_.id=? for update

[1] find = Registration : 1
[1] sleep
...
Hibernate:
select
registrati0_.id as id41_0_,
registrati0_.date_request as date2_41_0_,
registrati0_.email as email41_0_
from
cmn_registration registrati0_
where
registrati0_.id=? for update

[0] find = Registration : 1
Hibernate:
update
cmn_registration
set
date_request=?,
email=?
where
id=?
[0] updated Fri Jul 23 15:14:21 MSD 2010
Hibernate:
update
cmn_registration
set
date_request=?,
email=?
where
id=?
[1] updated Fri Jul 23 15:14:26 MSD 2010

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
[1] поток со sleep, [0] поток без sleep.
Видно, что когда [1] делает select for update, то [0] поток (запущенный сразу после [1]) отрабатывает обновление не дожидаясь [1] потока.

судя по запросам вроде все правильно, может какие-то проблемы с MySQL ? Или я просто не понимаю как должен работать select for update

Проблема с пессимистичными блокировками оказывается в том, что в MySQL/InnoDb SELECT FOR UPDATE работает только при выключенном автокоммите (SET AUTOCOMMIT = 0). Но в таком варианте после UPDATE придется делать COMMIT транзакции.

Осталось разобраться с оптимистичными блокировками
jonny81. фигасе