Почему мой пессимистический Блокировка в JPA с Oracle не работает
Я пытаюсь реализовать какие-то семафоры для заданий cron, которые работают в разных узлах JBoss. Я пытаюсь использовать базу данных (Oracle 11g) в качестве механизма блокировки, используя одну таблицу, чтобы синхронизировать задания cron в разных узлах.
Таблица очень проста:
CREATE TABLE SYNCHRONIZED_CRON_JOB_TASK
(
ID NUMBER(10) NOT NULL,
CRONJOBTYPE VARCHAR2(255 Byte),
CREATIONDATE TIMESTAMP(6) NOT NULL,
RUNNING NUMBER(1)
);
ALTER TABLE SYNCHRONIZED_CRON_JOB_TASK
ADD CONSTRAINT PK_SYNCHRONIZED_CRON_JOB_TASK
PRIMARY KEY (ID);
Итак, когда начинается задание, он ищет в таблице запись своего cronjobtype и проверяет, запущен ли он. Если он не обновляет флажок установки параметра, установленный в true. Этот первый выбор выполняется с помощью JPA CriteriaApi с использованием Hibernate и Pessimistic Lock.
query.setLockMode(javax.persistence.LockModeType.PESSIMISTIC_WRITE);
Все эти операции выполняются в течение одной транзакции.
Когда выполняется один из процессов, запросы, которые он делает, следующие:
[Server:server-two] 10:38:00,049 INFO [stdout] (scheduler-2) 2015-04-30 10:38:00,048 WARN (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes
[Server:server-two] 10:38:00,049 INFO [stdout] (scheduler-2) Hibernate: select * from ( select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=? ) where rownum <= ?
[Server:server-two] 10:38:00,053 INFO [stdout] (scheduler-2) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update
[Server:server-two] 10:38:00,056 INFO [stdout] (scheduler-2) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=?
Нет никаких проблем с этим предупреждением, вы можете увидеть первый выбор, а затем выбрать для обновления, поэтому Oracle должен блокировать другие операции выбора в этой строке.
Но что точка, запросы не блокируются, поэтому два задания могут вводить и делать выбор и обновление без проблем. Блокировка не работает, мы можем видеть это, если мы одновременно запускаем два задания cron:
[Server:server-one] 10:38:00,008 INFO [stdout] (scheduler-3) 2015-04-30 10:38:00,008 WARN (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes
[Server:server-two] 10:38:00,008 INFO [stdout] (scheduler-2) 2015-04-30 10:38:00,008 WARN (Loader.java:264) - HHH000444: Encountered request for locking however dialect reports that database prefers locking be done in a separate select (follow-on locking); results will be locked after initial query executes
[Server:server-two] 10:38:00,009 INFO [stdout] (scheduler-2) Hibernate: select * from ( select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=? ) where rownum <= ?
[Server:server-one] 10:38:00,009 INFO [stdout] (scheduler-3) Hibernate: select * from ( select distinct synchroniz0_.id as id1_127_, synchroniz0_.creationDate as creation2_127_, synchroniz0_.running as running3_127_, synchroniz0_.CRONJOBTYPE as CRONJOBT4_127_ from SYNCHRONIZED_CRON_JOB_TASK synchroniz0_ where synchroniz0_.CRONJOBTYPE=? ) where rownum <= ?
[Server:server-two] 10:38:00,013 INFO [stdout] (scheduler-2) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update
[Server:server-one] 10:38:00,014 INFO [stdout] (scheduler-3) Hibernate: select id from SYNCHRONIZED_CRON_JOB_TASK where id =? for update
[Server:server-two] 10:38:00,016 INFO [stdout] (scheduler-2) 2015-04-30 10:38:00,015 DEBUG (SynchronizedCronJobService.java:65) - Task read SynchronizedCronJobTask [id=185, type=AlertMailTaskExecutor, creationDate=2015-04-25 07:11:33.0, running=false]
[Server:server-two] 10:38:00,018 INFO [stdout] (scheduler-2) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=?
[Server:server-one] 10:38:00,022 INFO [stdout] (scheduler-3) 2015-04-30 10:38:00,022 DEBUG (SynchronizedCronJobService.java:65) - Task read SynchronizedCronJobTask [id=185, type=AlertMailTaskExecutor, creationDate=2015-04-25 07:11:33.0, running=false]
[Server:server-one] 10:38:00,024 INFO [stdout] (scheduler-3) Hibernate: update SYNCHRONIZED_CRON_JOB_TASK set creationDate=?, running=?, CRONJOBTYPE=? where id=?
Я попытался сделать этот выбор для обновления на инструменте SQL (SQLWorkbenchJ) двумя соединениями, и bloking работает отлично в этом инструменте. Но если я сделаю этот выбор для обновления на инструменте SQL и запустим задания cron, они не будут замаскированы и будут работать без проблем.
Я думаю, проблема связана с JPA, Hibernate или драйвером Oracle, но я не уверен. Любая идея о том, где проблема? Должен ли я использовать стратегию anotehr?
Спасибо заранее.
Ответы
Ответ 1
Наконец, мне удалось заставить его работать, но с некоторыми модификациями. Идея заключается в использовании LockModeType.PESSIMISTIC_FORCE_INCREMENT вместо PESSIMISTIC_WRITE. Используя этот режим блокировки, Cron Jobs ведут себя следующим образом:
- Когда первое задание делает выбор для обновления, все идет так, как ожидалось, но изменяется версия на объекте.
- Если другое задание пытается сделать тот же самый выбор, пока первый все еще находится в транзакции, JPA запускает исключение OptimisticLockException, поэтому, если вы поймаете это исключение, вы можете быть уверены, что оно было брошено для блокировки чтения.
Это решение имеет разные копии:
- SynchronizedCronJobTask должен иметь поле версии и находиться под управлением версии с помощью @Version
- Вам нужно обработать исключение OptimisticLockException, и оно должно быть уловлено вне метода транзакционной службы, чтобы выполнить откат при блокировке.
- IMHO - это не элегантное решение, намного хуже, чем просто блокировка, где Cron Jobs ждут завершения предыдущих Рабочих дней.
Ответ 2
Установите режим блокировки на PESSIMISTIC_READ, потому что вам нужен, чтобы второй сервер знал об изменениях первого сервера перед изменением данных.